1 module d.parser.statement; 2 3 import d.ast.declaration; 4 import d.ast.expression; 5 import d.ast.statement; 6 import d.ast.type; 7 8 import d.parser.ambiguous; 9 import d.parser.base; 10 import d.parser.conditional; 11 import d.parser.declaration; 12 import d.parser.expression; 13 import d.parser.type; 14 15 Statement parseStatement(ref TokenRange trange) { 16 Location location = trange.front.location; 17 18 switch (trange.front.type) with(TokenType) { 19 case OpenBrace: 20 return trange.parseBlock(); 21 22 case Identifier: 23 auto lookahead = trange.getLookahead(); 24 lookahead.popFront(); 25 26 if (lookahead.front.type == Colon) { 27 goto ParseLabel; 28 } 29 30 // If it is not a labeled statement, 31 // then it is a declaration or an expression. 32 goto default; 33 34 case If: 35 trange.popFront(); 36 trange.match(OpenParen); 37 38 auto condition = trange.parseExpression(); 39 40 trange.match(CloseParen); 41 42 auto then = trange.parseStatement(); 43 44 Statement elseStatement; 45 if (trange.front.type == Else) { 46 trange.popFront(); 47 48 elseStatement = trange.parseStatement(); 49 50 location.spanTo(elseStatement.location); 51 } else { 52 location.spanTo(then.location); 53 } 54 55 return new IfStatement(location, condition, then, elseStatement); 56 57 case While: 58 trange.popFront(); 59 trange.match(OpenParen); 60 auto condition = trange.parseExpression(); 61 62 trange.match(CloseParen); 63 64 auto statement = trange.parseStatement(); 65 66 location.spanTo(statement.location); 67 return new WhileStatement(location, condition, statement); 68 69 case Do: 70 trange.popFront(); 71 72 auto statement = trange.parseStatement(); 73 74 trange.match(While); 75 trange.match(OpenParen); 76 auto condition = trange.parseExpression(); 77 78 trange.match(CloseParen); 79 trange.match(Semicolon); 80 81 location.spanTo(trange.previous); 82 return new DoWhileStatement(location, condition, statement); 83 84 case For: 85 trange.popFront(); 86 87 trange.match(OpenParen); 88 89 Statement init; 90 if (trange.front.type != Semicolon) { 91 init = trange.parseStatement(); 92 } else { 93 trange.popFront(); 94 } 95 96 AstExpression condition; 97 if (trange.front.type != Semicolon) { 98 condition = trange.parseExpression(); 99 } 100 101 trange.match(Semicolon); 102 103 AstExpression increment; 104 if (trange.front.type != CloseParen) { 105 increment = trange.parseExpression(); 106 } 107 108 trange.match(CloseParen); 109 110 auto statement = trange.parseStatement(); 111 112 location.spanTo(statement.location); 113 return new ForStatement(location, init, condition, increment, statement); 114 115 case Foreach, ForeachReverse: 116 bool reverse = (trange.front.type == ForeachReverse); 117 trange.popFront(); 118 trange.match(OpenParen); 119 120 ParamDecl parseForeachListElement() { 121 Location elementLocation = trange.front.location; 122 123 bool isRef = trange.front.type == Ref; 124 if (isRef) { 125 trange.popFront(); 126 } 127 128 bool parseType = true; 129 // If we have an idientifer, check if the type is implicit. 130 if (trange.front.type == Identifier) { 131 auto lookahead = trange.getLookahead(); 132 lookahead.popFront(); 133 if (lookahead.front.type == Comma || lookahead.front.type == Semicolon) { 134 parseType = false; 135 } 136 } 137 138 auto type = parseType 139 ? trange.parseType() 140 : AstType.getAuto(); 141 142 auto name = trange.front.name; 143 elementLocation.spanTo(trange.front.location); 144 145 trange.match(Identifier); 146 147 return ParamDecl( 148 elementLocation, 149 type.getParamType(isRef ? ParamKind.Ref : ParamKind.Regular), 150 name, 151 null, 152 ); 153 } 154 155 ParamDecl[] tupleElements = [parseForeachListElement()]; 156 while(trange.front.type == Comma) { 157 trange.popFront(); 158 tupleElements ~= parseForeachListElement(); 159 } 160 161 trange.match(Semicolon); 162 auto iterrated = trange.parseExpression(); 163 164 bool isRange = trange.front.type == DotDot; 165 166 AstExpression endOfRange; 167 if (isRange) { 168 trange.popFront(); 169 endOfRange = trange.parseExpression(); 170 } 171 172 trange.match(CloseParen); 173 174 auto statement = trange.parseStatement(); 175 location.spanTo(statement.location); 176 177 return isRange 178 ? new ForeachRangeStatement( 179 location, 180 tupleElements, 181 iterrated, 182 endOfRange, 183 statement, 184 reverse, 185 ) 186 : new ForeachStatement( 187 location, 188 tupleElements, 189 iterrated, 190 statement, 191 reverse, 192 ); 193 194 case Return: 195 trange.popFront(); 196 197 AstExpression value; 198 if (trange.front.type != Semicolon) { 199 value = trange.parseExpression(); 200 } 201 202 trange.match(Semicolon); 203 204 location.spanTo(trange.previous); 205 return new ReturnStatement(location, value); 206 207 case Break: 208 trange.popFront(); 209 if (trange.front.type == Identifier) { 210 trange.popFront(); 211 } 212 213 trange.match(Semicolon); 214 215 location.spanTo(trange.previous); 216 return new BreakStatement(location); 217 218 case Continue: 219 trange.popFront(); 220 if (trange.front.type == Identifier) { 221 trange.popFront(); 222 } 223 224 trange.match(Semicolon); 225 226 location.spanTo(trange.previous); 227 return new ContinueStatement(location); 228 229 case Switch: 230 trange.popFront(); 231 trange.match(OpenParen); 232 233 auto expression = trange.parseExpression(); 234 235 trange.match(CloseParen); 236 237 auto statement = trange.parseStatement(); 238 location.spanTo(statement.location); 239 240 return new SwitchStatement(location, expression, statement); 241 242 case Case: 243 trange.popFront(); 244 245 AstExpression[] cases = trange.parseArguments(); 246 247 trange.match(Colon); 248 location.spanTo(trange.front.location); 249 250 Statement statement; 251 if (trange.front.type != CloseBrace) { 252 statement = trange.parseStatement(); 253 location.spanTo(statement.location); 254 } 255 256 location.spanTo(trange.previous); 257 return new CaseStatement(location, cases, statement); 258 259 case Default: 260 ParseLabel: 261 // Other labeled statement will jump here ! 262 auto label = trange.front.name; 263 trange.popFront(); 264 trange.match(Colon); 265 location.spanTo(trange.front.location); 266 267 Statement statement; 268 if (trange.front.type != CloseBrace) { 269 statement = trange.parseStatement(); 270 location.spanTo(statement.location); 271 } 272 273 return new LabeledStatement(location, label, statement); 274 275 case Goto: 276 trange.popFront(); 277 278 import source.name; 279 Name label; 280 switch(trange.front.type) { 281 case Identifier: 282 case Default: 283 case Case: 284 label = trange.front.name; 285 trange.popFront(); 286 break; 287 288 default: 289 trange.match(Identifier); 290 } 291 292 trange.match(Semicolon); 293 294 location.spanTo(trange.previous); 295 return new GotoStatement(location, label); 296 297 case Scope: 298 trange.popFront(); 299 trange.match(OpenParen); 300 301 auto name = trange.front.name; 302 trange.match(Identifier); 303 304 import source.name; 305 ScopeKind kind; 306 if (name == BuiltinName!"exit") { 307 kind = ScopeKind.Exit; 308 } else if (name == BuiltinName!"success") { 309 kind = ScopeKind.Success; 310 } else if (name == BuiltinName!"failure") { 311 kind = ScopeKind.Failure; 312 } else { 313 assert(0, name.toString(trange.context) ~ " is not a valid scope identifier."); 314 } 315 316 trange.match(CloseParen); 317 318 auto statement = trange.parseStatement(); 319 location.spanTo(statement.location); 320 321 return new ScopeStatement(location, kind, statement); 322 323 case Assert: 324 trange.popFront(); 325 trange.match(OpenParen); 326 327 auto condition = trange.parseAssignExpression(); 328 AstExpression message; 329 if (trange.front.type == Comma) { 330 trange.popFront(); 331 message = trange.parseAssignExpression(); 332 333 // Trailing comma 334 if (trange.front.type == Comma) { 335 trange.popFront(); 336 } 337 } 338 339 trange.match(CloseParen); 340 trange.match(Semicolon); 341 342 location.spanTo(trange.previous); 343 return new AssertStatement(location, condition, message); 344 345 case Throw: 346 trange.popFront(); 347 auto value = trange.parseExpression(); 348 349 trange.match(Semicolon); 350 351 location.spanTo(trange.previous); 352 return new ThrowStatement(location, value); 353 354 case Try: 355 trange.popFront(); 356 357 auto statement = trange.parseStatement(); 358 359 CatchBlock[] catches; 360 while(trange.front.type == Catch) { 361 auto catchLocation = trange.front.location; 362 trange.popFront(); 363 364 if (trange.front.type == OpenParen) { 365 trange.popFront(); 366 367 import d.parser.identifier; 368 auto type = trange.parseIdentifier(); 369 370 import source.name; 371 Name name; 372 if (trange.front.type == Identifier) { 373 name = trange.front.name; 374 trange.popFront(); 375 } 376 377 trange.match(CloseParen); 378 379 auto catchStatement = trange.parseStatement(); 380 381 location.spanTo(catchStatement.location); 382 catches ~= CatchBlock(location, type, name, catchStatement); 383 } else { 384 // TODO: handle final catches ? 385 trange.parseStatement(); 386 assert(0, "Final catches not implemented"); 387 } 388 } 389 390 Statement finallyStatement; 391 if (trange.front.type == Finally) { 392 trange.popFront(); 393 finallyStatement = trange.parseStatement(); 394 } 395 396 location.spanTo(trange.previous); 397 return new TryStatement(location, statement, catches, finallyStatement); 398 399 case Synchronized: 400 trange.popFront(); 401 if (trange.front.type == OpenParen) { 402 trange.popFront(); 403 trange.parseExpression(); 404 trange.match(CloseParen); 405 } 406 407 auto statement = trange.parseStatement(); 408 location.spanTo(statement.location); 409 410 return new SynchronizedStatement(location, statement); 411 412 case Mixin: 413 trange.popFront(); 414 trange.match(OpenParen); 415 416 auto expr = trange.parseAssignExpression(); 417 418 // To deambiguate vs TokenType.Mixin. 419 import d.ast.conditional : Mixin; 420 421 trange.match(CloseParen); 422 if (trange.front.type == Semicolon) { 423 // mixin(expr); is a statement. 424 location.spanTo(trange.front.location); 425 trange.popFront(); 426 427 return new Mixin!Statement(location, expr); 428 } 429 430 location.spanTo(trange.previous); 431 expr = new Mixin!AstExpression(location, expr); 432 return trange.parseStatementSuffix(expr); 433 434 case Static: 435 auto lookahead = trange.getLookahead(); 436 lookahead.popFront(); 437 438 switch (lookahead.front.type) { 439 case If: 440 return trange.parseStaticIf!Statement(); 441 442 case Assert: 443 return trange.parseStaticAssert!Statement(); 444 445 default: 446 auto declaration = trange.parseDeclaration(); 447 return new DeclarationStatement(declaration); 448 } 449 450 case Version: 451 return trange.parseVersion!Statement(); 452 453 case Debug: 454 return trange.parseDebug!Statement(); 455 456 default: 457 return trange.parseAmbiguousStatement(); 458 } 459 460 assert(0); 461 } 462 463 BlockStatement parseBlock(ref TokenRange trange) { 464 Location location = trange.front.location; 465 466 trange.match(TokenType.OpenBrace); 467 468 Statement[] statements; 469 470 while (trange.front.type != TokenType.CloseBrace) { 471 statements ~= trange.parseStatement(); 472 } 473 474 trange.popFront(); 475 476 location.spanTo(trange.previous); 477 return new BlockStatement(location, statements); 478 }