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.tmpl;
10 
11 import vdc.util;
12 import vdc.lexer;
13 import vdc.ast.node;
14 import vdc.ast.decl;
15 import vdc.ast.expr;
16 import vdc.ast.type;
17 import vdc.ast.writer;
18 import vdc.interpret;
19 import vdc.semantic;
20 
21 import stdext.util;
22 
23 //TemplateDeclaration:
24 //    [Identifier TemplateParameterList Constraint_opt DeclarationBlock]
25 class TemplateDeclaration : Node
26 {
27     mixin ForwardCtor!();
28 
29     Identifier getIdentifier() { return getMember!Identifier(0); }
30     TemplateParameterList getTemplateParameterList() { return getMember!TemplateParameterList(1); }
31     Constraint getConstraint() { return members.length > 3 ? getMember!Constraint(2) : null; }
32     Node getBody() { return getMember(members.length - 1); }
33     bool isMixin() { return id == TOK_mixin; }
34 
35     override TemplateDeclaration clone()
36     {
37         TemplateDeclaration n = static_cast!TemplateDeclaration(super.clone());
38         return n;
39     }
40 
41     override void toD(CodeWriter writer)
42     {
43         if(isMixin())
44             writer("mixin ");
45         writer("template ", getIdentifier(), getTemplateParameterList());
46         writer.nl();
47         if(getConstraint())
48         {
49             writer(getConstraint());
50             writer.nl();
51         }
52 //        writer("{");
53 //        writer.nl();
54 //        {
55 //            CodeIndenter indent = CodeIndenter(writer);
56             writer(getBody());
57 //        }
58 //        writer("}");
59 //        writer.nl();
60         writer.nl();
61     }
62     override void toC(CodeWriter writer)
63     {
64         // we never write the template, only instantiations
65     }
66 
67     override void addSymbols(Scope sc)
68     {
69         string ident = getIdentifier().ident;
70         sc.addSymbol(ident, this);
71     }
72 
73     override void _semantic(Scope sc)
74     {
75         // do not recurse into declaration, it only makes sense for an instance
76     }
77 
78     override bool isTemplate() const { return true; }
79 
80     override Node expandTemplate(Scope sc, TemplateArgumentList args)
81     {
82         TemplateParameterList tpl = getTemplateParameterList();
83         string ident = getIdentifier().ident;
84 
85         ArgMatch[] vargs = matchTemplateArgs(ident, sc, args, tpl);
86         ParameterList pl = createTemplateParameterList(vargs);
87 
88         auto bdy = getBody().clone();
89         auto inst = new TemplateMixinInstance;
90         inst.addMember(pl);
91         inst.addMember(bdy);
92         return inst;
93     }
94 }
95 
96 //TemplateMixinInstance:
97 //    name [ParameterList DeclarationBlock]
98 class TemplateMixinInstance : Type
99 {
100     mixin ForwardCtor!();
101 
102     // semantic data
103     string instanceName; // set when named instance created by cloning
104     TypeValue typeVal;
105 
106     ParameterList getTemplateParameterList() { return getMember!ParameterList(0); }
107     Node getBody() { return getMember(1); }
108 
109     override bool propertyNeedsParens() const { return false; }
110 
111     override void toD(CodeWriter writer)
112     {
113         writer("mixin ", getBody(), " ", instanceName);
114     }
115 
116     override void _semantic(Scope sc)
117     {
118         // TODO: TemplateParameterList, Constraint
119         sc = enterScope(sc);
120         super._semantic(sc);
121         sc = sc.pop();
122     }
123 
124     override void addSymbols(Scope sc)
125     {
126         if(instanceName.length)
127             sc.addSymbol(instanceName, this);
128         else
129         {
130             sc = enterScope(sc).pop();
131 
132             // put symbols into parent scope aswell
133             foreach(id, sym; scop.symbols)
134                 foreach(s, b; sym)
135                     sc.addSymbol(id, s);
136         }
137     }
138 
139     override Type calcType()
140     {
141         return this;
142     }
143 
144     override Value interpret(Context sc)
145     {
146         if(!typeVal)
147             typeVal = new TypeValue(calcType());
148         return typeVal;
149     }
150 }
151 
152 //TemplateParameterList:
153 //    [ TemplateParameter... ]
154 class TemplateParameterList : Node
155 {
156     mixin ForwardCtor!();
157 
158     override void toD(CodeWriter writer)
159     {
160         writer("(");
161         writer.writeArray(members);
162         writer(")");
163     }
164 }
165 
166 //TemplateParameter:
167 //    TemplateTypeParameter
168 //    TemplateValueParameter
169 //    TemplateAliasParameter
170 //    TemplateTupleParameter
171 //    TemplateThisParameter
172 class TemplateParameter : Node
173 {
174     mixin ForwardCtor!();
175 }
176 
177 //TemplateInstance:
178 //    ident [ TemplateArgumentList ]
179 class TemplateInstance : Identifier
180 {
181     mixin ForwardCtorTok!();
182 
183     TemplateArgumentList getTemplateArgumentList() { return getMember!TemplateArgumentList(0); }
184 
185     override void toD(CodeWriter writer)
186     {
187         writer.writeIdentifier(ident);
188         writer("!(", getMember(0), ")");
189     }
190 
191     override Value interpret(Context sc)
192     {
193         return super.interpret(sc);
194     }
195 }
196 //
197 //
198 //TemplateArgumentList:
199 //    [ TemplateArgument... ]
200 class TemplateArgumentList : Node
201 {
202     mixin ForwardCtorNoId!();
203 
204     override void toD(CodeWriter writer)
205     {
206         bool writeSep = false;
207         foreach(m; members)
208         {
209             if(writeSep)
210                 writer(", ");
211             writeSep = true;
212 
213             bool paren = false;
214             if(auto expr = cast(Expression) m)
215                 paren = (expr.getPrecedence() <= PREC.expr);
216 
217             if(paren)
218                 writer("(", m, ")");
219             else
220                 writer(m);
221         }
222     }
223 }
224 
225 //
226 //TemplateArgument:
227 //    Type
228 //    AssignExpression
229 //    Symbol
230 //
231 //// identical to IdentifierList
232 //Symbol:
233 //    SymbolTail
234 //    . SymbolTail
235 //
236 //SymbolTail:
237 //    Identifier
238 //    Identifier . SymbolTail
239 //    TemplateInstance
240 //    TemplateInstance . SymbolTail
241 //
242 //TemplateSingleArgument:
243 //    Identifier
244 //    BasicTypeX
245 //    CharacterLiteral
246 //    StringLiteral
247 //    IntegerLiteral
248 //    FloatLiteral
249 //    true
250 //    false
251 //    null
252 //    __FILE__
253 //    __LINE__
254 
255 //TemplateTypeParameter:
256 //    Identifier
257 //    Identifier TemplateTypeParameterSpecialization
258 //    Identifier TemplateTypeParameterDefault
259 //    Identifier TemplateTypeParameterSpecialization TemplateTypeParameterDefault
260 class TemplateTypeParameter : TemplateParameter
261 {
262     string ident;
263     Type specialization;
264     Node def;
265 
266     this() {} // default constructor needed for clone()
267 
268     this(Token tok)
269     {
270         super(tok);
271         ident = tok.txt;
272     }
273 
274     override TemplateTypeParameter clone()
275     {
276         TemplateTypeParameter n = static_cast!TemplateTypeParameter(super.clone());
277         n.ident = ident;
278         for(int m = 0; m < members.length; m++)
279         {
280             if(members[m] is specialization)
281                 n.specialization = static_cast!Type(n.members[m]);
282             if(members[m] is def)
283                 n.def = n.members[m];
284         }
285         return n;
286     }
287     override bool compare(const(Node) n) const
288     {
289         if(!super.compare(n))
290             return false;
291 
292         auto tn = static_cast!(typeof(this))(n);
293         return tn.ident == ident;
294     }
295 
296     override void toD(CodeWriter writer)
297     {
298         writer.writeIdentifier(ident);
299         if(specialization)
300             writer(" : ", specialization);
301         if(def)
302             writer(" = ", def);
303     }
304 }
305 
306 //TemplateTypeParameterSpecialization:
307 //    : Type
308 //
309 //TemplateTypeParameterDefault:
310 //    = Type
311 
312 //TemplateThisParameter:
313 //    [ TemplateTypeParameter ]
314 class TemplateThisParameter : TemplateParameter
315 {
316     mixin ForwardCtor!();
317 
318     override void toD(CodeWriter writer)
319     {
320         writer("this ", getMember(0));
321     }
322 }
323 //
324 //TemplateValueParameter:
325 //    Declaration
326 //    Declaration TemplateValueParameterSpecialization
327 //    Declaration TemplateValueParameterDefault
328 //    Declaration TemplateValueParameterSpecialization TemplateValueParameterDefault
329 class TemplateValueParameter : TemplateParameter
330 {
331     mixin ForwardCtor!();
332 
333     Expression specialization;
334     Expression def;
335 
336     ParameterDeclarator getParameterDeclarator() { return getMember!ParameterDeclarator(0); }
337 
338     override TemplateValueParameter clone()
339     {
340         TemplateValueParameter n = static_cast!TemplateValueParameter(super.clone());
341         for(int m = 0; m < members.length; m++)
342         {
343             if(members[m] is specialization)
344                 n.specialization = static_cast!Expression(n.members[m]);
345             if(members[m] is def)
346                 n.def = static_cast!Expression(n.members[m]);
347         }
348         return n;
349     }
350 
351     override void toD(CodeWriter writer)
352     {
353         writer(getMember(0));
354         if(specialization)
355             writer(" : ", specialization);
356         if(def)
357             writer(" = ", def);
358     }
359 }
360 //
361 //TemplateValueParameterSpecialization:
362 //    : ConditionalExpression
363 //
364 //TemplateValueParameterDefault:
365 //    = __FILE__
366 //    = __LINE__
367 //    = ConditionalExpression
368 //
369 //TemplateAliasParameter:
370 //    alias Identifier TemplateAliasParameterSpecialization_opt TemplateAliasParameterDefault_opt
371 //
372 //TemplateAliasParameterSpecialization:
373 //    : Type
374 //
375 //TemplateAliasParameterDefault:
376 //    = Type
377 class TemplateAliasParameter : TemplateParameter
378 {
379     mixin ForwardCtor!();
380 
381     string getIdent() { return getMember!TemplateTypeParameter(0).ident; }
382 
383     override void toD(CodeWriter writer)
384     {
385         writer("alias ", getMember(0));
386     }
387 }
388 //
389 //TemplateTupleParameter:
390 //    Identifier ...
391 class TemplateTupleParameter : TemplateParameter
392 {
393     string ident;
394 
395     override TemplateTupleParameter clone()
396     {
397         TemplateTupleParameter n = static_cast!TemplateTupleParameter(super.clone());
398         n.ident = ident;
399         return n;
400     }
401     override bool compare(const(Node) n) const
402     {
403         if(!super.compare(n))
404             return false;
405 
406         auto tn = static_cast!(typeof(this))(n);
407         return tn.ident == ident;
408     }
409 
410     this() {} // default constructor needed for clone()
411 
412     this(Token tok)
413     {
414         super(tok);
415         ident = tok.txt;
416     }
417     override void toD(CodeWriter writer)
418     {
419         writer.writeIdentifier(ident);
420         writer("...");
421     }
422 }
423 //
424 //ClassTemplateDeclaration:
425 //    class Identifier ( TemplateParameterList ) BaseClassList_opt ClassBody
426 //
427 //InterfaceTemplateDeclaration:
428 //    interface Identifier ( TemplateParameterList ) Constraint_opt BaseInterfaceList_opt InterfaceBody
429 //
430 //TemplateMixinDeclaration:
431 //    mixin template TemplateIdentifier ( TemplateParameterList ) Constraint_opt { DeclDefs }
432 
433 //TemplateMixin:
434 //    mixin TemplateIdentifier ;
435 //    mixin TemplateIdentifier MixinIdentifier ;
436 //    mixin TemplateIdentifier ! ( TemplateArgumentList ) ;
437 //    mixin TemplateIdentifier ! ( TemplateArgumentList ) MixinIdentifier ;
438 //
439 // translated to
440 //TemplateMixin:
441 //    [IdentifierList MixinIdentifier_opt]
442 //    [Typeof MixinIdentifier]
443 class TemplateMixin : Node
444 {
445     mixin ForwardCtor!();
446 
447     override void toD(CodeWriter writer)
448     {
449         writer("mixin ", getMember(0));
450         if(members.length > 1)
451             writer(" ", getMember(1));
452         writer(";");
453         writer.nl();
454     }
455 
456     override Node[] expandNonScopeInterpret(Scope sc, Node[] athis)
457     {
458         Node tmpl = getMember(0);
459         Node n;
460         if(auto prop = cast(TypeProperty) tmpl)
461             n = prop.resolve();
462         else if(auto idlist = cast(IdentifierList) tmpl)
463             n = idlist.doResolve(true);
464 
465         if(!n)
466             semanticError("cannot resolve ", tmpl);
467         else if(auto tmi = cast(TemplateMixinInstance) n)
468         {
469             // TODO: match constraints, replace parameters
470             if(members.length > 1)
471             {
472                 // named instance
473                 string name = getMember!Identifier(1).ident;
474                 tmi.instanceName = name;
475             }
476             return [ tmi ];
477         }
478         else
479             semanticError(n, " is not a TemplateMixinInstance");
480         return athis;
481     }
482 }
483 
484 //
485 //Constraint:
486 //    if ( ConstraintExpression )
487 class Constraint : Node
488 {
489     mixin ForwardCtor!();
490 
491     override void toD(CodeWriter writer)
492     {
493         writer(" if(", getMember(0), ")");
494     }
495 }
496 //
497 //ConstraintExpression:
498 //    Expression
499 //
500 //MixinIdentifier:
501 //    Identifier
502 //
503 
504 ArgMatch[] matchTemplateArgs(string ident, Scope sc, TemplateArgumentList args, TemplateParameterList tpl)
505 {
506     if(args.members.length != tpl.members.length)
507     {
508         semanticError("incorrect number of arguments for template expansion of ", ident);
509         return null;
510     }
511     ArgMatch[] vargs;
512     Context ctx = new Context(nullContext);
513     ctx.scop = sc;
514     int m;
515     for(m = 0; m < args.members.length; m++)
516     {
517         Value v;
518         string name;
519         auto am = args.members[m];
520         auto pm = tpl.members[m];
521         if(auto typeparam = cast(TemplateTypeParameter) pm)
522         {
523             v = am.interpret(ctx);
524             name = typeparam.ident;
525             if(!cast(TypeValue) v)
526             {
527                 semanticError(ident, ": ", m+1, ". argument must evaluate to a type, not ", v.toStr());
528                 v = null;
529             }
530         }
531         else if(auto thisparam = cast(TemplateThisParameter) pm)
532         {
533             semanticError("cannot infer this parameter for ", ident);
534         }
535         else if(auto valueparam = cast(TemplateValueParameter) pm)
536         {
537             v = am.interpret(ctx);
538             auto decl = valueparam.getParameterDeclarator().getDeclarator();
539             v = decl.calcType().createValue(ctx, v);
540             name = decl.ident;
541         }
542         else if(auto aliasparam = cast(TemplateAliasParameter) pm)
543         {
544             if(auto idtype = cast(IdentifierType) am)
545                 v = new AliasValue(idtype.getIdentifierList());
546             else if(auto type = cast(Type) am)
547                 v = new TypeValue(type);
548             else if(auto id = cast(IdentifierExpression) am)
549             {
550                 auto idlist = new IdentifierList;
551                 idlist.addMember(id.getIdentifier().clone());
552                 v = new AliasValue(idlist);
553             }
554             else
555                 semanticError(ident, ": ", m+1, ". argument must evaluate to an identifier, not ", am);
556             name = aliasparam.getIdent();
557         }
558         else if(auto tupleparam = cast(TemplateTupleParameter) pm)
559         {
560             semanticError("cannot infer template tuple parameter for ", ident);
561         }
562         if(!v)
563             return null;
564         vargs ~= ArgMatch(v, name);
565     }
566     return vargs;
567 }
568 
569 
570 ParameterList createTemplateParameterList(ArgMatch[] vargs)
571 {
572     ParameterList pl = new ParameterList;
573     for(int m = 0; m < vargs.length; m++)
574     {
575         auto pd = new ParameterDeclarator;
576         pd.addMember(vargs[m].value.getType().clone());
577 
578         auto d = new Declarator;
579         d.ident = vargs[m].name;
580         if(auto av = cast(AliasValue) vargs[m].value)
581         {
582             d.isAlias = true;
583             d.aliasTo = av.resolve();
584         }
585         else
586         {
587             d.value = vargs[m].value;
588         }
589         pd.addMember(d);
590         pl.addMember(pd);
591     }
592     return pl;
593 }