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 }