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 }