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.misc;
10 
11 import vdc.lexer;
12 import vdc.semantic;
13 import vdc.interpret;
14 import vdc.util;
15 
16 import vdc.ast.node;
17 import vdc.ast.expr;
18 import vdc.ast.decl;
19 import vdc.ast.stmt;
20 import vdc.ast.type;
21 import vdc.ast.writer;
22 
23 import stdext.util;
24 
25 import std.algorithm;
26 
27 //EnumDeclaration:
28 //    enum EnumTag EnumBody
29 //    enum EnumBody
30 //    enum EnumTag : EnumBaseType EnumBody
31 //    enum : EnumBaseType EnumBody
32 //    enum Identifier = AssignExpression ;
33 //
34 //EnumTag:
35 //    Identifier
36 //
37 //EnumBaseType:
38 //    Type
39 class EnumDeclaration : Type
40 {
41     mixin ForwardCtor!();
42 
43     string ident;
44     bool isDecl; // does not have body syntax
45 
46     override bool propertyNeedsParens() const { return false; }
47 
48     override EnumDeclaration clone()
49     {
50         EnumDeclaration n = static_cast!EnumDeclaration(super.clone());
51         n.ident = ident;
52         n.isDecl = isDecl;
53         return n;
54     }
55     override bool compare(const(Node) n) const
56     {
57         if(!super.compare(n))
58             return false;
59 
60         auto tn = static_cast!(typeof(this))(n);
61         return tn.isDecl == isDecl
62             && tn.ident == ident;
63     }
64 
65     Type getBaseType() { return members.length > 1 ? getMember!Type(0) : null; }
66     EnumBody getBody() { return members.length > 0 ? getMember!EnumBody(members.length - 1) : null; }
67 
68     override void toD(CodeWriter writer)
69     {
70         if(!writer.writeDeclarations)
71             return;
72         if(writer.writeReferencedOnly)
73         {
74             if(ident.length)
75             {
76                 if(semanticSearches == 0)
77                     return;
78             }
79             else if(auto bdy = getBody())
80                 if(!bdy.hasSemanticSearches())
81                     return;
82         }
83         if(isDecl)
84         {
85             writer("enum ");
86             if (auto type = getBaseType())
87                 writer(type, " ");
88             writer.writeArray(getBody().getEnumMembers().members);
89             writer(";");
90             writer.nl;
91         }
92         else
93         {
94             writer("enum ");
95             writer.writeIdentifier(ident);
96             if(writer.writeClassImplementations)
97             {
98                 if(Type type = getBaseType())
99                     writer(" : ", type);
100                 if (members.length > 0)
101                 {
102                     writer.nl();
103                     writer(getBody());
104                 }
105                 else
106                 {
107                     writer(";");
108                     writer.nl;
109                 }
110             }
111         }
112     }
113 
114     override bool createsScope() const { return ident.length > 0; }
115 
116     override void addSymbols(Scope sc)
117     {
118         if(ident.length)
119             sc.addSymbol(ident, this);
120 
121         else if(auto bdy = getBody())
122             bdy.addSymbols(sc);
123     }
124 
125     override Value createValue(Context ctx, Value initValue)
126     {
127         if(auto bt = getBaseType())
128             return getBaseType().createValue(ctx, initValue);
129         if(initValue)
130             return initValue.getType().createValue(ctx, initValue);
131         return Value.create(0);
132     }
133 }
134 
135 // forward declaration not needed with proper handling
136 //EnumBody:
137 //    ;
138 //    { EnumMembers }
139 class EnumBody : Node
140 {
141     mixin ForwardCtor!();
142 
143     EnumMembers getEnumMembers() { return getMember!EnumMembers(0); }
144 
145     override void toD(CodeWriter writer)
146     {
147         writer("{");
148         writer.nl();
149         {
150             CodeIndenter indent = CodeIndenter(writer);
151             writer(getMember(0));
152         }
153         writer("}");
154         writer.nl();
155     }
156 
157     bool hasSemanticSearches()
158     {
159         return getEnumMembers().hasSemanticSearches();
160     }
161 
162     override void addSymbols(Scope sc)
163     {
164         getMember(0).addSymbols(sc);
165     }
166 }
167 
168 //EnumMembers:
169 //    EnumMember
170 //    EnumMember ,
171 //    EnumMember , EnumMembers
172 class EnumMembers : Node
173 {
174     mixin ForwardCtor!();
175 
176     override void toD(CodeWriter writer)
177     {
178         foreach(m; members)
179         {
180             writer(m, ",");
181             writer.nl();
182         }
183     }
184 
185     bool hasSemanticSearches()
186     {
187         foreach(m; members)
188             if(m.semanticSearches > 0)
189                 return true;
190         return false;
191     }
192 
193     override void addSymbols(Scope sc)
194     {
195         addMemberSymbols(sc);
196     }
197 }
198 
199 //EnumMember:
200 //    Identifier
201 //    Identifier = AssignExpression
202 //    Type Identifier = AssignExpression
203 class EnumMember : Node
204 {
205     mixin ForwardCtor!();
206 
207     string ident;
208     Type type;
209     Value value;
210 
211     override EnumMember clone()
212     {
213         EnumMember n = static_cast!EnumMember(super.clone());
214         n.ident = ident;
215         return n;
216     }
217     override bool compare(const(Node) n) const
218     {
219         if(!super.compare(n))
220             return false;
221 
222         auto tn = static_cast!(typeof(this))(n);
223         return tn.ident == ident;
224     }
225 
226     string getIdentifier() { return ident; }
227     Expression getInitializer() { return members.length > 0 ? getMember!Expression(members.length - 1) : null; }
228     Type getType() { return members.length > 1 ? getMember!Type(0) : null; }
229 
230     override Type calcType()
231     {
232         if(type)
233             return type;
234 
235         if(auto dtype = getType())
236             type = dtype.calcType();
237         else if(parent && parent.parent && parent.parent)
238             if(auto ed = cast(EnumDeclaration)parent.parent.parent)
239                 type = ed.calcType();
240 
241         if(!type)
242             type = semanticErrorType("cannot determine type of enum member ", ident);
243         return type;
244     }
245 
246     override void toD(CodeWriter writer)
247     {
248         if(Type type = getType())
249             writer(type, " ");
250         writer.writeIdentifier(ident);
251         if(auto expr = getInitializer())
252             writer(" = ", expr);
253     }
254 
255     override void addSymbols(Scope sc)
256     {
257         sc.addSymbol(ident, this);
258     }
259 
260     override Value interpret(Context sc)
261     {
262         if(value)
263             return value;
264 
265         Value ival;
266         if(Expression expr = getInitializer())
267             ival = expr.interpret(sc);
268         else if(auto em = cast(EnumMembers)parent)
269         {
270             auto n = countUntil(parent.members, this);
271             if(n > 0)
272             {
273                 ival = parent.members[n - 1].interpret(sc);
274                 ival = ival.opBin(sc, TOK_add, Value.create(cast(byte)1));
275             }
276         }
277         value = calcType().createValue(sc, ival);
278         return value;
279     }
280 }
281 
282 ////////////////////////////////////////////////////////////////
283 //FunctionBody:
284 //    [InStatement_opt OutStatement_opt BodyStatement] outIdentifier
285 class FunctionBody : Node
286 {
287     mixin ForwardCtor!();
288 
289     Statement inStatement;
290     Statement outStatement;
291     Statement bodyStatement;
292     OutIdentifier outIdentifier;
293 
294     Scope inScop;
295     Scope outScop;
296 
297     override FunctionBody clone()
298     {
299         FunctionBody n = static_cast!FunctionBody(super.clone());
300         for(int m = 0; m < members.length; m++)
301         {
302             if(members[m] is inStatement)
303                 n.inStatement = static_cast!Statement(n.members[m]);
304             if(members[m] is outStatement)
305                 n.outStatement = static_cast!Statement(n.members[m]);
306             if(members[m] is bodyStatement)
307                 n.bodyStatement = static_cast!Statement(n.members[m]);
308             if(members[m] is outIdentifier)
309                 n.outIdentifier = static_cast!OutIdentifier(n.members[m]);
310         }
311         return n;
312     }
313 
314     override void toD(CodeWriter writer)
315     {
316         if(inStatement)
317         {
318             writer("in");
319             writer.nl();
320             writer(inStatement);
321         }
322         if(outStatement)
323         {
324             if(outIdentifier)
325                 writer("out(", outIdentifier, ")");
326             else
327                 writer("out");
328             writer.nl();
329             writer(outStatement);
330         }
331         if(bodyStatement)
332         {
333             if(inStatement || outStatement)
334             {
335                 writer("body");
336                 writer.nl();
337             }
338             writer(bodyStatement);
339         }
340         writer.nl; // should not be written for function literals
341     }
342 
343     override bool createsScope() const { return true; }
344 
345     override Scope enterScope(ref Scope nscope, Scope sc)
346     {
347         if(!nscope)
348         {
349             nscope = new Scope;
350             nscope.annotations = sc.annotations;
351             nscope.attributes = sc.attributes;
352             nscope.mod = sc.mod;
353             nscope.parent = sc;
354             nscope.node = this;
355 
356             ParameterList pl;
357             if(auto callable = cast(CallableNode) parent)
358                 pl = callable.getParameterList();
359             if(auto decl = cast(Decl) parent)
360                 if(auto decls = decl.getDeclarators())
361                     if(auto callable = cast(CallableNode) decls.getDeclarator(0))
362                         pl = callable.getParameterList();
363             if(pl)
364                 pl.addSymbols(nscope);
365             return nscope;
366         }
367         return sc.push(nscope);
368     }
369 
370     override void _semantic(Scope sc)
371     {
372         if(inStatement)
373         {
374             sc = enterScope(inScop, sc);
375             inStatement.semantic(sc);
376             sc = sc.pop();
377         }
378         if(bodyStatement)
379         {
380             sc = super.enterScope(sc);
381             bodyStatement.semantic(sc);
382             sc = sc.pop();
383         }
384         if(outStatement)
385         {
386             // TODO: put into scope of inStatement?
387             sc = enterScope(outScop, sc);
388             if(outIdentifier)
389                 sc.addSymbol(outIdentifier.ident, outIdentifier); // TODO: create Symbol for outIdentifier
390             outStatement.semantic(sc);
391             sc = sc.pop();
392         }
393     }
394 
395     override Value interpret(Context sc)
396     {
397         Value value;
398         if(inStatement)
399         {
400             inStatement.interpret(sc);
401         }
402         if(bodyStatement)
403         {
404             value = bodyStatement.interpret(sc);
405         }
406         if(outStatement)
407         {
408             // TODO: put into scope of inStatement?
409             outStatement.interpret(sc);
410         }
411         if(!value)
412             return theVoidValue;
413         return value;
414     }
415 }
416 
417 class OutIdentifier : Identifier
418 {
419     mixin ForwardCtorTok!();
420 
421     override Type calcType()
422     {
423         auto fb = cast(FunctionBody)parent;
424         if(fb)
425         {
426             auto type = fb.parent.calcType();
427             if(auto tf = cast(TypeFunction) type)
428                 return tf.getReturnType();
429         }
430         return semanticErrorType("cannot calculate type of out identifier ", ident);
431     }
432 }
433 
434 ////////////////////////////////////////////////////////////////
435 class ConditionalDeclaration : Node
436 {
437     mixin ForwardCtor!();
438 
439     Condition getCondition() { return getMember!Condition(0); }
440     Node getThenDeclarations() { return getMember(1); }
441     Node getElseDeclarations() { return getMember(2); }
442 
443     override void toD(CodeWriter writer)
444     {
445         writer(getMember(0));
446         if(id == TOK_colon)
447             writer(":");
448         writer.nl;
449         {
450             CodeIndenter indent = CodeIndenter(writer);
451             writer(getMember(1));
452         }
453         if(members.length > 2)
454         {
455             writer("else");
456             writer.nl;
457             {
458                 CodeIndenter indent = CodeIndenter(writer);
459                 writer(getMember(2));
460             }
461         }
462     }
463 
464     override Node[] expandNonScopeBlock(Scope sc, Node[] athis)
465     {
466         Node n;
467         if(getCondition().evalCondition(sc))
468             n = getThenDeclarations();
469         else
470             n = getElseDeclarations();
471         if(!n)
472             return null;
473         athis[0] = removeMember(n);
474         return athis;
475     }
476 }
477 
478 class ConditionalStatement : Statement
479 {
480     mixin ForwardCtor!();
481 
482     Condition getCondition() { return getMember!Condition(0); }
483     Statement getThenStatement() { return getMember!Statement(1); }
484     Statement getElseStatement() { return getMember!Statement(2); }
485 
486     override void toD(CodeWriter writer)
487     {
488         writer(getMember(0));
489         writer.nl;
490         {
491             CodeIndenter indent = CodeIndenter(writer);
492             writer(getMember(1));
493         }
494         if(members.length > 2)
495         {
496             writer("else");
497             writer.nl;
498             {
499                 CodeIndenter indent = CodeIndenter(writer);
500                 writer(getMember(2));
501             }
502         }
503     }
504 
505     override Node[] expandNonScopeBlock(Scope sc, Node[] athis)
506     {
507         if(cast(StaticIfCondition) getCondition())
508             return athis;
509 
510         Node n;
511         if(getCondition().evalCondition(sc))
512             n = getThenStatement();
513         else
514             n = getElseStatement();
515         if(!n)
516             return null;
517         athis[0] = removeMember(n);
518         return athis;
519     }
520 
521     override Node[] expandNonScopeInterpret(Scope sc, Node[] athis)
522     {
523         if(!cast(StaticIfCondition) getCondition())
524             return athis;
525 
526         Node n;
527         if(getCondition().evalCondition(sc))
528             n = getThenStatement();
529         else
530             n = getElseStatement();
531         if(!n)
532             return null;
533         athis[0] = n;
534         return athis;
535     }
536 }
537 
538 mixin template GetIdentifierOrInteger(int pos = 0)
539 {
540     bool isIdentifier() { return getMember(pos).id == TOK_Identifier; }
541     string getIdentifier() { return getMember!Identifier(pos).ident; }
542     int getInteger() { return getMember!IntegerLiteralExpression(pos).getInt(); }
543 }
544 
545 class VersionSpecification : Node
546 {
547     mixin ForwardCtor!();
548     mixin GetIdentifierOrInteger!();
549 
550     override void toD(CodeWriter writer)
551     {
552         writer("version = ", getMember(0), ";");
553         writer.nl;
554     }
555 
556     override Node[] expandNonScopeBlock(Scope sc, Node[] athis)
557     {
558         auto mod = sc.mod;
559         if(isIdentifier())
560             mod.specifyVersion(getIdentifier(), span.start);
561         else
562             mod.specifyVersion(getInteger());
563         return [];
564     }
565 }
566 
567 class DebugSpecification : Node
568 {
569     mixin ForwardCtor!();
570     mixin GetIdentifierOrInteger!();
571 
572     override void toD(CodeWriter writer)
573     {
574         writer("debug = ", getMember(0), ";");
575         writer.nl;
576     }
577 
578     override Node[] expandNonScopeBlock(Scope sc, Node[] athis)
579     {
580         auto mod = sc.mod;
581         if(isIdentifier())
582             mod.specifyDebug(getIdentifier(), span.start);
583         else
584             mod.specifyDebug(getInteger());
585         return [];
586     }
587 }
588 
589 class Condition : Node
590 {
591     mixin ForwardCtor!();
592 
593     abstract bool evalCondition(Scope sc);
594 }
595 
596 class VersionCondition : Condition
597 {
598     mixin ForwardCtor!();
599     mixin GetIdentifierOrInteger!();
600 
601     override bool evalCondition(Scope sc)
602     {
603         if(members.length == 0)
604         {
605             assert(id == TOK_unittest || id == TOK_assert);
606             if(auto mod = getModule())
607                 if(auto prj = mod.getProject())
608                     return prj.options.unittestOn || (id == TOK_assert && prj.options.debugOn);
609             return false;
610         }
611         auto mod = getModule();
612         if(isIdentifier())
613             return mod.versionEnabled(getIdentifier(), span.start);
614         return mod.versionEnabled(getInteger());
615     }
616 
617     override void toD(CodeWriter writer)
618     {
619         if(members.length > 0)
620             writer("version(", getMember(0), ") ");
621         else
622         {
623             assert(id == TOK_unittest);
624             writer("version(", id, ")");
625         }
626     }
627 }
628 
629 class DebugCondition : Condition
630 {
631     mixin ForwardCtor!();
632     mixin GetIdentifierOrInteger!();
633 
634     override bool evalCondition(Scope sc)
635     {
636         auto mod = getModule();
637         if(members.length == 0)
638             return mod.debugEnabled();
639         if(isIdentifier())
640             return mod.debugEnabled(getIdentifier(), span.start);
641         return mod.debugEnabled(getInteger());
642     }
643 
644     override void toD(CodeWriter writer)
645     {
646         if(members.length > 0)
647             writer("debug(", getMember(0), ") ");
648         else
649             writer("debug ");
650     }
651 }
652 
653 class StaticIfCondition : Condition
654 {
655     mixin ForwardCtor!();
656 
657     override bool evalCondition(Scope sc)
658     {
659         Context ctx = new Context(nullContext);
660         ctx.scop = sc;
661         return getMember!Expression(0).interpret(ctx).toBool();
662     }
663 
664     override void toD(CodeWriter writer)
665     {
666         writer("static if(", getMember(0), ")");
667     }
668 }
669 
670 //Aggregate:
671 //    [ArgumentList]
672 class StaticAssert : Statement
673 {
674     mixin ForwardCtor!();
675 
676     ArgumentList getArgumentList() { return getMember!ArgumentList(0); }
677 
678     override void toD(CodeWriter writer)
679     {
680         if(writer.writeImplementations)
681         {
682             writer("static assert(", getMember(0), ");");
683             writer.nl();
684         }
685     }
686     override void toC(CodeWriter writer)
687     {
688     }
689 
690     override void _semantic(Scope sc)
691     {
692         auto args = getArgumentList();
693         auto expr = args.getMember!Expression(0);
694         if(!expr.interpretCatch(globalContext).toBool())
695         {
696             string txt;
697             for(int a = 1; a < args.members.length; a++)
698             {
699                 auto arg = args.getMember!Expression(a);
700                 txt ~= arg.interpret(globalContext).toMixin();
701             }
702             if(txt.length == 0)
703                 txt = "static assertion " ~ writeD(expr) ~ " failed";
704             semanticErrorPos(span.start, txt);
705         }
706     }
707 
708     override Value interpret(Context sc)
709     {
710         return null; // "execution" done in _sementic
711     }
712 }