1 /// Author: Aziz Köksal
2 /// License: GPL3
3 /// $(Maturity low)
4 module dil.semantic.Pass2;
5 
6 import dil.ast.DefaultVisitor,
7        dil.ast.Node,
8        dil.ast.Declarations,
9        dil.ast.Expressions,
10        dil.ast.Statements,
11        dil.ast.Types,
12        dil.ast.Parameters;
13 import dil.semantic.Symbol,
14        dil.semantic.Symbols,
15        dil.semantic.Types,
16        dil.semantic.Scope,
17        dil.semantic.Module,
18        dil.semantic.Analysis;
19 import dil.code.Interpreter;
20 import dil.lexer.Identifier,
21        dil.lexer.IDsEnum;
22 import dil.parser.Parser;
23 import dil.i18n.Messages;
24 import dil.SourceText,
25        dil.Diagnostics,
26        dil.Enums;
27 import common;
28 
29 /// The second pass determines the types of symbols and the types
30 /// of expressions and also evaluates them.
31 class SemanticPass2 : DefaultVisitor
32 {
33   Scope scop; /// The current scope.
34   Module modul; /// The module to be semantically checked.
35 
36   /// Constructs a SemanticPass2 object.
37   /// Params:
38   ///   modul = The module to be checked.
39   this(Module modul)
40   {
41     this.modul = modul;
42   }
43 
44   /// Start semantic analysis.
45   void run()
46   {
47     assert(modul.root !is null);
48     // Create module scope.
49     scop = new Scope(null, modul);
50     modul.semanticPass = 2;
51     visit(modul.root);
52   }
53 
54   /// Enters a new scope.
55   void enterScope(ScopeSymbol s)
56   {
57     scop = scop.enter(s);
58   }
59 
60   /// Exits the current scope.
61   void exitScope()
62   {
63     scop = scop.exit();
64   }
65 
66   /// Evaluates e and returns the result.
67   Expression interpret(Expression e)
68   {
69     return Interpreter.interpret(e, modul.cc.diag);
70   }
71 
72   /// Creates an error report.
73   void error(Token* token, MID mid, ...)
74   {
75     auto location = token.getErrorLocation(modul.filePath());
76     auto msg = modul.cc.diag.formatMsg(mid, _arguments, _argptr);
77     modul.cc.diag ~= new SemanticError(location, msg);
78   }
79 
80   /// Some handy aliases.
81   private alias D = Declaration;
82   private alias E = Expression; /// ditto
83   private alias S = Statement; /// ditto
84   private alias T = TypeNode; /// ditto
85 
86   /// The current scope symbol to use for looking up identifiers.
87   ///
88   /// E.g.:
89   /// ---
90   /// / // * "object" is looked up in the current scope.
91   /// / // * idScope is set if "object" is a ScopeSymbol.
92   /// / // * "method" will be looked up in idScope.
93   /// object.method();
94   /// / // * "dil" is looked up in the current scope
95   /// / // * idScope is set if "dil" is a ScopeSymbol.
96   /// / // * "ast" will be looked up in idScope.
97   /// / // * idScope is set if "ast" is a ScopeSymbol.
98   /// / // * etc.
99   /// dil.ast.Node.Node node;
100   /// ---
101   ScopeSymbol idScope;
102 
103   /// Searches for a symbol.
104   Symbol search(Token* idTok)
105   {
106     assert(idTok.kind == TOK.Identifier);
107     auto id = idTok.ident;
108     Symbol symbol;
109 
110     if (idScope is null)
111       symbol = scop.search(id);
112     else
113       symbol = idScope.lookup(id);
114 
115     if (symbol is null)
116       error(idTok, MID.UndefinedIdentifier, id.str);
117     else if (auto scopSymbol = cast(ScopeSymbol)symbol)
118       idScope = scopSymbol;
119 
120     return symbol;
121   }
122 
123 override
124 {
125   //alias visit = super.visit;
126 
127   D visit(CompoundDecl d)
128   {
129     return super.visit(d);
130   }
131 
132   D visit(EnumDecl d)
133   {
134     d.symbol.setCompleting();
135 
136     Type type = Types.Int32; // Default to int.
137     if (d.baseType)
138       type = visitT(d.baseType).type;
139     // Set the enum's base type.
140     d.symbol.type.baseType = type;
141 
142     // TODO: check base type. must be basic type or another enum.
143 
144     enterScope(d.symbol);
145 
146     foreach (member; d.members)
147     {
148       Expression finalValue;
149       member.symbol.setCompleting();
150       if (member.value)
151       {
152         member.value = visitE(member.value);
153         finalValue = interpret(member.value);
154         if (finalValue is Interpreter.NAR)
155           finalValue = new IntExpr(0, d.symbol.type);
156       }
157       //else
158         // TODO: increment a number variable and assign that to value.
159       member.symbol.value = finalValue;
160       member.symbol.setComplete();
161     }
162 
163     exitScope();
164     d.symbol.setComplete();
165     return d;
166   }
167 
168   D visit(MixinDecl md)
169   {
170     if (md.decls)
171       return md.decls;
172     if (md.isMixinExpr)
173     {
174       md.argument = visitE(md.argument);
175       auto expr = interpret(md.argument);
176       if (expr is Interpreter.NAR)
177         return md;
178       auto stringExpr = expr.Is!(StringExpr);
179       if (stringExpr is null)
180       {
181         error(md.begin, MID.MixinArgumentMustBeString);
182         return md;
183       }
184       else
185       { // Parse the declarations in the string.
186         auto loc = md.begin.getErrorLocation(modul.filePath());
187         auto filePath = loc.filePath;
188         auto sourceText = new SourceText(filePath, stringExpr.getString().dup);
189         auto lxtables = modul.cc.tables.lxtables;
190         auto parser = new Parser(sourceText, lxtables, modul.cc.diag);
191         md.decls = parser.start();
192       }
193     }
194     else
195     {
196       // TODO: implement template mixin.
197     }
198     return md.decls;
199   }
200 
201   // Type nodes:
202 
203   T visit(TypeofType t)
204   {
205     t.expr = visitE(t.expr);
206     t.type = t.expr.type;
207     return t;
208   }
209 
210   T visit(ArrayType t)
211   {
212     auto baseType = visitT(t.next).type;
213     if (t.isAssociative)
214       t.type = baseType.arrayOf(visitT(t.assocType).type);
215     else if (t.isDynamic)
216       t.type = baseType.arrayOf();
217     else if (t.isStatic)
218     {}
219     else
220       assert(t.isSlice);
221     return t;
222   }
223 
224   T visit(PointerType t)
225   {
226     t.type = visitT(t.next).type.ptrTo();
227     return t;
228   }
229 
230   T visit(IdentifierType t)
231   {
232     auto idToken = t.begin;
233     auto symbol = search(idToken);
234     // TODO: save symbol or its type in t.
235     return t;
236   }
237 
238   T visit(TmplInstanceType t)
239   {
240     auto idToken = t.begin;
241     auto symbol = search(idToken);
242     // TODO: save symbol or its type in t.
243     return t;
244   }
245 
246   T visit(ModuleScopeType t)
247   {
248     idScope = modul;
249     return t;
250   }
251 
252   T visit(IntegralType t)
253   {
254     t.type = Types.fromTOK(t.tok);
255     return t;
256   }
257 
258   // Expression nodes:
259 
260   E visit(ParenExpr e)
261   {
262     if (!e.type)
263     {
264       e.next = visitE(e.next);
265       e.type = e.next.type;
266     }
267     return e;
268   }
269 
270   E visit(CommaExpr e)
271   {
272     if (!e.type)
273     {
274       e.lhs = visitE(e.lhs);
275       e.rhs = visitE(e.rhs);
276       e.type = e.rhs.type;
277     }
278     return e;
279   }
280 
281   E visit(OrOrExpr)
282   { return null; }
283 
284   E visit(AndAndExpr)
285   { return null; }
286 
287   E visit(SpecialTokenExpr e)
288   {
289     if (e.type)
290       return e.value;
291     switch (e.specialToken.ident.idKind)
292     {
293     case IDK.LINE, IDK.VERSION:
294       e.value = new IntExpr(e.specialToken.uint_, Types.UInt32);
295       break;
296     case IDK.FILE, IDK.DATE, IDK.TIME, IDK.TIMESTAMP, IDK.VENDOR:
297       e.value = new StringExpr(e.specialToken.strval.str);
298       break;
299     default:
300       assert(0);
301     }
302     e.type = e.value.type;
303     return e.value;
304   }
305 
306   E visit(DollarExpr e)
307   {
308     if (e.type)
309       return e;
310     e.type = modul.cc.tables.types.Size_t;
311     // if (!inArraySubscript)
312     //   error("$ can only be in an array subscript.");
313     return e;
314   }
315 
316   E visit(NullExpr e)
317   {
318     if (!e.type)
319       e.type = Types.Void_ptr;
320     return e;
321   }
322 
323   E visit(BoolExpr e)
324   {
325     if (e.type)
326       return e;
327     e.value = new IntExpr(e.toBool(), Types.Bool);
328     e.type = Types.Bool;
329     return e;
330   }
331 
332   E visit(IntExpr e)
333   {
334     if (e.type)
335       return e;
336 
337     if (e.number & 0x8000_0000_0000_0000)
338       e.type = Types.UInt64; // 0xFFFF_FFFF_FFFF_FFFF
339     else if (e.number & 0xFFFF_FFFF_0000_0000)
340       e.type = Types.Int64; // 0x7FFF_FFFF_FFFF_FFFF
341     else if (e.number & 0x8000_0000)
342       e.type = Types.UInt32; // 0xFFFF_FFFF
343     else
344       e.type = Types.Int32; // 0x7FFF_FFFF
345     return e;
346   }
347 
348   E visit(FloatExpr e)
349   {
350     if (!e.type)
351       e.type = Types.Float64;
352     return e;
353   }
354 
355   E visit(ComplexExpr e)
356   {
357     if (!e.type)
358       e.type = Types.CFloat64;
359     return e;
360   }
361 
362   E visit(CharExpr e)
363   {
364     return e;
365   }
366 
367   E visit(StringExpr e)
368   {
369     return e;
370   }
371 
372   E visit(MixinExpr me)
373   {
374     if (me.type)
375       return me.expr;
376     me.expr = visitE(me.expr);
377     auto expr = interpret(me.expr);
378     if (expr is Interpreter.NAR)
379       return me;
380     auto stringExpr = expr.Is!(StringExpr);
381     if (stringExpr is null)
382      error(me.begin, MID.MixinArgumentMustBeString);
383     else
384     {
385       auto loc = me.begin.getErrorLocation(modul.filePath());
386       auto filePath = loc.filePath;
387       auto sourceText = new SourceText(filePath, stringExpr.getString().dup);
388       auto lxtables = modul.cc.tables.lxtables;
389       auto parser = new Parser(sourceText, lxtables, modul.cc.diag);
390       expr = parser.start2();
391       expr = visitE(expr); // Check expression.
392     }
393     me.expr = expr;
394     me.type = expr.type;
395     return me.expr;
396   }
397 
398   E visit(ImportExpr ie)
399   {
400     if (ie.type)
401       return ie.expr;
402     ie.expr = visitE(ie.expr);
403     auto expr = interpret(ie.expr);
404     if (expr is Interpreter.NAR)
405       return ie;
406     auto stringExpr = expr.Is!(StringExpr);
407     //if (stringExpr is null)
408     //  error(me.begin, MID.ImportArgumentMustBeString);
409     // TODO: load file
410     //ie.expr = new StringExpr(loadImportFile(stringExpr.getString()));
411     return ie.expr;
412   }
413 }
414 }