diff options
Diffstat (limited to 'Parse/ParseStmt.cpp')
-rw-r--r-- | Parse/ParseStmt.cpp | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/Parse/ParseStmt.cpp b/Parse/ParseStmt.cpp new file mode 100644 index 0000000000..7bc7a9c9a7 --- /dev/null +++ b/Parse/ParseStmt.cpp @@ -0,0 +1,848 @@ +//===--- ParseStmt.cpp - Statement and Block Parser -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Statement and Block portions of the Parser +// interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/Parse/Parser.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Parse/DeclSpec.h" +#include "clang/Parse/Scope.h" +using namespace clang; + +//===----------------------------------------------------------------------===// +// C99 6.8: Statements and Blocks. +//===----------------------------------------------------------------------===// + +/// ParseStatementOrDeclaration - Read 'statement' or 'declaration'. +/// StatementOrDeclaration: +/// statement +/// declaration +/// +/// statement: +/// labeled-statement +/// compound-statement +/// expression-statement +/// selection-statement +/// iteration-statement +/// jump-statement +/// [OBC] objc-throw-statement [TODO] +/// [OBC] objc-try-catch-statement [TODO] +/// [OBC] objc-synchronized-statement [TODO] +/// [GNU] asm-statement +/// [OMP] openmp-construct [TODO] +/// +/// labeled-statement: +/// identifier ':' statement +/// 'case' constant-expression ':' statement +/// 'default' ':' statement +/// +/// selection-statement: +/// if-statement +/// switch-statement +/// +/// iteration-statement: +/// while-statement +/// do-statement +/// for-statement +/// +/// expression-statement: +/// expression[opt] ';' +/// +/// jump-statement: +/// 'goto' identifier ';' +/// 'continue' ';' +/// 'break' ';' +/// 'return' expression[opt] ';' +/// [GNU] 'goto' '*' expression ';' +/// +/// [OBC] objc-throw-statement: [TODO] +/// [OBC] '@' 'throw' expression ';' [TODO] +/// [OBC] '@' 'throw' ';' [TODO] +/// +Parser::StmtResult Parser::ParseStatementOrDeclaration(bool OnlyStatement) { + const char *SemiError = 0; + Parser::StmtResult Res; + + // Cases in this switch statement should fall through if the parser expects + // the token to end in a semicolon (in which case SemiError should be set), + // or they directly 'return;' if not. + switch (Tok.getKind()) { + case tok::identifier: // C99 6.8.1: labeled-statement + // identifier ':' statement + // declaration (if !OnlyStatement) + // expression[opt] ';' + return ParseIdentifierStatement(OnlyStatement); + + default: + if (!OnlyStatement && isDeclarationSpecifier()) { + // TODO: warn/disable if declaration is in the middle of a block and !C99. + return Actions.ParseDeclStmt(ParseDeclaration(Declarator::BlockContext)); + } else if (Tok.getKind() == tok::r_brace) { + Diag(Tok, diag::err_expected_statement); + return true; + } else { + // expression[opt] ';' + ExprResult Res = ParseExpression(); + if (Res.isInvalid) { + // If the expression is invalid, skip ahead to the next semicolon. Not + // doing this opens us up to the possibility of infinite loops if + // ParseExpression does not consume any tokens. + SkipUntil(tok::semi); + return true; + } + // Otherwise, eat the semicolon. + ExpectAndConsume(tok::semi, diag::err_expected_semi_after_expr); + return Actions.ParseExprStmt(Res.Val); + } + + case tok::kw_case: // C99 6.8.1: labeled-statement + return ParseCaseStatement(); + case tok::kw_default: // C99 6.8.1: labeled-statement + return ParseDefaultStatement(); + + case tok::l_brace: // C99 6.8.2: compound-statement + return ParseCompoundStatement(); + case tok::semi: // C99 6.8.3p3: expression[opt] ';' + return Actions.ParseNullStmt(ConsumeToken()); + + case tok::kw_if: // C99 6.8.4.1: if-statement + return ParseIfStatement(); + case tok::kw_switch: // C99 6.8.4.2: switch-statement + return ParseSwitchStatement(); + + case tok::kw_while: // C99 6.8.5.1: while-statement + return ParseWhileStatement(); + case tok::kw_do: // C99 6.8.5.2: do-statement + Res = ParseDoStatement(); + SemiError = "do/while loop"; + break; + case tok::kw_for: // C99 6.8.5.3: for-statement + return ParseForStatement(); + + case tok::kw_goto: // C99 6.8.6.1: goto-statement + Res = ParseGotoStatement(); + SemiError = "goto statement"; + break; + case tok::kw_continue: // C99 6.8.6.2: continue-statement + Res = ParseContinueStatement(); + SemiError = "continue statement"; + break; + case tok::kw_break: // C99 6.8.6.3: break-statement + Res = ParseBreakStatement(); + SemiError = "break statement"; + break; + case tok::kw_return: // C99 6.8.6.4: return-statement + Res = ParseReturnStatement(); + SemiError = "return statement"; + break; + + case tok::kw_asm: + Res = ParseAsmStatement(); + SemiError = "asm statement"; + break; + } + + // If we reached this code, the statement must end in a semicolon. + if (Tok.getKind() == tok::semi) { + ConsumeToken(); + } else { + Diag(Tok, diag::err_expected_semi_after, SemiError); + SkipUntil(tok::semi); + } + return Res; +} + +/// ParseIdentifierStatement - Because we don't have two-token lookahead, we +/// have a bit of a quandry here. Reading the identifier is necessary to see if +/// there is a ':' after it. If there is, this is a label, regardless of what +/// else the identifier can mean. If not, this is either part of a declaration +/// (if the identifier is a type-name) or part of an expression. +/// +/// labeled-statement: +/// identifier ':' statement +/// [GNU] identifier ':' attributes[opt] statement +/// declaration (if !OnlyStatement) +/// expression[opt] ';' +/// +Parser::StmtResult Parser::ParseIdentifierStatement(bool OnlyStatement) { + assert(Tok.getKind() == tok::identifier && Tok.getIdentifierInfo() && + "Not an identifier!"); + + LexerToken IdentTok = Tok; // Save the whole token. + ConsumeToken(); // eat the identifier. + + // identifier ':' statement + if (Tok.getKind() == tok::colon) { + SourceLocation ColonLoc = ConsumeToken(); + + // Read label attributes, if present. + DeclTy *AttrList = 0; + if (Tok.getKind() == tok::kw___attribute) + // TODO: save these somewhere. + AttrList = ParseAttributes(); + + StmtResult SubStmt = ParseStatement(); + + // Broken substmt shouldn't prevent the label from being added to the AST. + if (SubStmt.isInvalid) + SubStmt = Actions.ParseNullStmt(ColonLoc); + + return Actions.ParseLabelStmt(IdentTok.getLocation(), + IdentTok.getIdentifierInfo(), + ColonLoc, SubStmt.Val); + } + + // Check to see if this is a declaration. + void *TypeRep; + if (!OnlyStatement && + (TypeRep = Actions.isTypeName(*IdentTok.getIdentifierInfo(), CurScope))) { + // Handle this. Warn/disable if in middle of block and !C99. + DeclSpec DS; + + // Add the typedef name to the start of the decl-specs. + const char *PrevSpec = 0; + int isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typedef, + IdentTok.getLocation(), PrevSpec, + TypeRep); + assert(!isInvalid && "First declspec can't be invalid!"); + + // ParseDeclarationSpecifiers will continue from there. + ParseDeclarationSpecifiers(DS); + + // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };" + // declaration-specifiers init-declarator-list[opt] ';' + if (Tok.getKind() == tok::semi) { + // TODO: emit error on 'int;' or 'const enum foo;'. + // if (!DS.isMissingDeclaratorOk()) Diag(...); + + ConsumeToken(); + // FIXME: Return this as a type decl. + return 0; + } + + // Parse all the declarators. + Declarator DeclaratorInfo(DS, Declarator::BlockContext); + ParseDeclarator(DeclaratorInfo); + + DeclTy *Decl = ParseInitDeclaratorListAfterFirstDeclarator(DeclaratorInfo); + return Decl ? Actions.ParseDeclStmt(Decl) : 0; + } + + // Otherwise, this is an expression. Seed it with II and parse it. + ExprResult Res = ParseExpressionWithLeadingIdentifier(IdentTok); + if (Res.isInvalid) { + SkipUntil(tok::semi); + return true; + } else if (Tok.getKind() != tok::semi) { + Diag(Tok, diag::err_expected_semi_after, "expression"); + SkipUntil(tok::semi); + return true; + } else { + ConsumeToken(); + // Convert expr to a stmt. + return Actions.ParseExprStmt(Res.Val); + } +} + +/// ParseCaseStatement +/// labeled-statement: +/// 'case' constant-expression ':' statement +/// [GNU] 'case' constant-expression '...' constant-expression ':' statement +/// +/// Note that this does not parse the 'statement' at the end. +/// +Parser::StmtResult Parser::ParseCaseStatement() { + assert(Tok.getKind() == tok::kw_case && "Not a case stmt!"); + SourceLocation CaseLoc = ConsumeToken(); // eat the 'case'. + + ExprResult LHS = ParseConstantExpression(); + if (LHS.isInvalid) { + SkipUntil(tok::colon); + return true; + } + + // GNU case range extension. + SourceLocation DotDotDotLoc; + ExprTy *RHSVal = 0; + if (Tok.getKind() == tok::ellipsis) { + Diag(Tok, diag::ext_gnu_case_range); + DotDotDotLoc = ConsumeToken(); + + ExprResult RHS = ParseConstantExpression(); + if (RHS.isInvalid) { + SkipUntil(tok::colon); + return true; + } + RHSVal = RHS.Val; + } + + if (Tok.getKind() != tok::colon) { + Diag(Tok, diag::err_expected_colon_after, "'case'"); + SkipUntil(tok::colon); + return true; + } + + SourceLocation ColonLoc = ConsumeToken(); + + // Diagnose the common error "switch (X) { case 4: }", which is not valid. + if (Tok.getKind() == tok::r_brace) { + Diag(Tok, diag::err_label_end_of_compound_statement); + return true; + } + + StmtResult SubStmt = ParseStatement(); + + // Broken substmt shouldn't prevent the case from being added to the AST. + if (SubStmt.isInvalid) + SubStmt = Actions.ParseNullStmt(ColonLoc); + + // TODO: look up enclosing switch stmt. + return Actions.ParseCaseStmt(CaseLoc, LHS.Val, DotDotDotLoc, RHSVal, ColonLoc, + SubStmt.Val); +} + +/// ParseDefaultStatement +/// labeled-statement: +/// 'default' ':' statement +/// Note that this does not parse the 'statement' at the end. +/// +Parser::StmtResult Parser::ParseDefaultStatement() { + assert(Tok.getKind() == tok::kw_default && "Not a default stmt!"); + SourceLocation DefaultLoc = ConsumeToken(); // eat the 'default'. + + if (Tok.getKind() != tok::colon) { + Diag(Tok, diag::err_expected_colon_after, "'default'"); + SkipUntil(tok::colon); + return true; + } + + SourceLocation ColonLoc = ConsumeToken(); + + // Diagnose the common error "switch (X) {... default: }", which is not valid. + if (Tok.getKind() == tok::r_brace) { + Diag(Tok, diag::err_label_end_of_compound_statement); + return true; + } + + StmtResult SubStmt = ParseStatement(); + if (SubStmt.isInvalid) + return true; + + // TODO: look up enclosing switch stmt. + return Actions.ParseDefaultStmt(DefaultLoc, ColonLoc, SubStmt.Val); +} + + +/// ParseCompoundStatement - Parse a "{}" block. +/// +/// compound-statement: [C99 6.8.2] +/// { block-item-list[opt] } +/// [GNU] { label-declarations block-item-list } [TODO] +/// +/// block-item-list: +/// block-item +/// block-item-list block-item +/// +/// block-item: +/// declaration +/// [GNU] '__extension__' declaration [TODO] +/// statement +/// [OMP] openmp-directive [TODO] +/// +/// [GNU] label-declarations: +/// [GNU] label-declaration +/// [GNU] label-declarations label-declaration +/// +/// [GNU] label-declaration: +/// [GNU] '__label__' identifier-list ';' +/// +/// [OMP] openmp-directive: [TODO] +/// [OMP] barrier-directive +/// [OMP] flush-directive +/// +Parser::StmtResult Parser::ParseCompoundStatement() { + assert(Tok.getKind() == tok::l_brace && "Not a compount stmt!"); + + // Enter a scope to hold everything within the compound stmt. + EnterScope(0); + + // Parse the statements in the body. + StmtResult Body = ParseCompoundStatementBody(); + + ExitScope(); + return Body; +} + + +/// ParseCompoundStatementBody - Parse a sequence of statements and invoke the +/// ParseCompoundStmt action. This expects the '{' to be the current token, and +/// consume the '}' at the end of the block. It does not manipulate the scope +/// stack. +Parser::StmtResult Parser::ParseCompoundStatementBody() { + SourceLocation LBraceLoc = ConsumeBrace(); // eat the '{'. + + // TODO: "__label__ X, Y, Z;" is the GNU "Local Label" extension. These are + // only allowed at the start of a compound stmt. + + llvm::SmallVector<StmtTy*, 32> Stmts; + while (Tok.getKind() != tok::r_brace && Tok.getKind() != tok::eof) { + StmtResult R = ParseStatementOrDeclaration(false); + if (!R.isInvalid && R.Val) + Stmts.push_back(R.Val); + } + + // We broke out of the while loop because we found a '}' or EOF. + if (Tok.getKind() != tok::r_brace) { + Diag(Tok, diag::err_expected_rbrace); + return 0; + } + + SourceLocation RBraceLoc = ConsumeBrace(); + return Actions.ParseCompoundStmt(LBraceLoc, RBraceLoc, + &Stmts[0], Stmts.size()); +} + +/// ParseIfStatement +/// if-statement: [C99 6.8.4.1] +/// 'if' '(' expression ')' statement +/// 'if' '(' expression ')' statement 'else' statement +/// +Parser::StmtResult Parser::ParseIfStatement() { + assert(Tok.getKind() == tok::kw_if && "Not an if stmt!"); + SourceLocation IfLoc = ConsumeToken(); // eat the 'if'. + + if (Tok.getKind() != tok::l_paren) { + Diag(Tok, diag::err_expected_lparen_after, "if"); + SkipUntil(tok::semi); + return true; + } + + // Parse the condition. + ExprResult CondExp = ParseSimpleParenExpression(); + if (CondExp.isInvalid) { + SkipUntil(tok::semi); + return true; + } + + // Read the if condition. + StmtResult CondStmt = ParseStatement(); + + // Broken substmt shouldn't prevent the label from being added to the AST. + if (CondStmt.isInvalid) + CondStmt = Actions.ParseNullStmt(Tok.getLocation()); + + + // If it has an else, parse it. + SourceLocation ElseLoc; + StmtResult ElseStmt(false); + if (Tok.getKind() == tok::kw_else) { + ElseLoc = ConsumeToken(); + ElseStmt = ParseStatement(); + + if (ElseStmt.isInvalid) + ElseStmt = Actions.ParseNullStmt(ElseLoc); + } + + return Actions.ParseIfStmt(IfLoc, CondExp.Val, CondStmt.Val, + ElseLoc, ElseStmt.Val); +} + +/// ParseSwitchStatement +/// switch-statement: +/// 'switch' '(' expression ')' statement +Parser::StmtResult Parser::ParseSwitchStatement() { + assert(Tok.getKind() == tok::kw_switch && "Not a switch stmt!"); + SourceLocation SwitchLoc = ConsumeToken(); // eat the 'switch'. + + if (Tok.getKind() != tok::l_paren) { + Diag(Tok, diag::err_expected_lparen_after, "switch"); + SkipUntil(tok::semi); + return true; + } + + // Start the switch scope. + EnterScope(Scope::BreakScope); + + // Parse the condition. + ExprResult Cond = ParseSimpleParenExpression(); + + // Read the body statement. + StmtResult Body = ParseStatement(); + + ExitScope(); + + if (Cond.isInvalid || Body.isInvalid) return true; + + return Actions.ParseSwitchStmt(SwitchLoc, Cond.Val, Body.Val); +} + +/// ParseWhileStatement +/// while-statement: [C99 6.8.5.1] +/// 'while' '(' expression ')' statement +Parser::StmtResult Parser::ParseWhileStatement() { + assert(Tok.getKind() == tok::kw_while && "Not a while stmt!"); + SourceLocation WhileLoc = Tok.getLocation(); + ConsumeToken(); // eat the 'while'. + + if (Tok.getKind() != tok::l_paren) { + Diag(Tok, diag::err_expected_lparen_after, "while"); + SkipUntil(tok::semi); + return true; + } + + // Start the loop scope. + EnterScope(Scope::BreakScope | Scope::ContinueScope); + + // Parse the condition. + ExprResult Cond = ParseSimpleParenExpression(); + + // Read the body statement. + StmtResult Body = ParseStatement(); + + ExitScope(); + + if (Cond.isInvalid || Body.isInvalid) return true; + + return Actions.ParseWhileStmt(WhileLoc, Cond.Val, Body.Val); +} + +/// ParseDoStatement +/// do-statement: [C99 6.8.5.2] +/// 'do' statement 'while' '(' expression ')' ';' +/// Note: this lets the caller parse the end ';'. +Parser::StmtResult Parser::ParseDoStatement() { + assert(Tok.getKind() == tok::kw_do && "Not a do stmt!"); + SourceLocation DoLoc = ConsumeToken(); // eat the 'do'. + + // Start the loop scope. + EnterScope(Scope::BreakScope | Scope::ContinueScope); + + // Read the body statement. + StmtResult Body = ParseStatement(); + + if (Tok.getKind() != tok::kw_while) { + ExitScope(); + Diag(Tok, diag::err_expected_while); + Diag(DoLoc, diag::err_matching, "do"); + SkipUntil(tok::semi); + return true; + } + SourceLocation WhileLoc = ConsumeToken(); + + if (Tok.getKind() != tok::l_paren) { + ExitScope(); + Diag(Tok, diag::err_expected_lparen_after, "do/while"); + SkipUntil(tok::semi); + return true; + } + + // Parse the condition. + ExprResult Cond = ParseSimpleParenExpression(); + + ExitScope(); + + if (Cond.isInvalid || Body.isInvalid) return true; + + return Actions.ParseDoStmt(DoLoc, Body.Val, WhileLoc, Cond.Val); +} + +/// ParseForStatement +/// for-statement: [C99 6.8.5.3] +/// 'for' '(' expr[opt] ';' expr[opt] ';' expr[opt] ')' statement +/// 'for' '(' declaration expr[opt] ';' expr[opt] ')' statement +Parser::StmtResult Parser::ParseForStatement() { + assert(Tok.getKind() == tok::kw_for && "Not a for stmt!"); + SourceLocation ForLoc = ConsumeToken(); // eat the 'for'. + + if (Tok.getKind() != tok::l_paren) { + Diag(Tok, diag::err_expected_lparen_after, "for"); + SkipUntil(tok::semi); + return true; + } + + EnterScope(Scope::BreakScope | Scope::ContinueScope); + + SourceLocation LParenLoc = ConsumeParen(); + ExprResult Value; + + StmtTy *FirstPart = 0; + ExprTy *SecondPart = 0; + StmtTy *ThirdPart = 0; + + // Parse the first part of the for specifier. + if (Tok.getKind() == tok::semi) { // for (; + // no first part, eat the ';'. + ConsumeToken(); + } else if (isDeclarationSpecifier()) { // for (int X = 4; + // Parse declaration, which eats the ';'. + if (!getLang().C99) // Use of C99-style for loops in C90 mode? + Diag(Tok, diag::ext_c99_variable_decl_in_for_loop); + DeclTy *aBlockVarDecl = ParseDeclaration(Declarator::ForContext); + StmtResult stmtResult = Actions.ParseDeclStmt(aBlockVarDecl); + FirstPart = stmtResult.isInvalid ? 0 : stmtResult.Val; + } else { + Value = ParseExpression(); + + // Turn the expression into a stmt. + if (!Value.isInvalid) { + StmtResult R = Actions.ParseExprStmt(Value.Val); + if (!R.isInvalid) + FirstPart = R.Val; + } + + if (Tok.getKind() == tok::semi) { + ConsumeToken(); + } else { + if (!Value.isInvalid) Diag(Tok, diag::err_expected_semi_for); + SkipUntil(tok::semi); + } + } + + // Parse the second part of the for specifier. + if (Tok.getKind() == tok::semi) { // for (...;; + // no second part. + Value = ExprResult(); + } else { + Value = ParseExpression(); + if (!Value.isInvalid) + SecondPart = Value.Val; + } + + if (Tok.getKind() == tok::semi) { + ConsumeToken(); + } else { + if (!Value.isInvalid) Diag(Tok, diag::err_expected_semi_for); + SkipUntil(tok::semi); + } + + // Parse the third part of the for specifier. + if (Tok.getKind() == tok::r_paren) { // for (...;...;) + // no third part. + Value = ExprResult(); + } else { + Value = ParseExpression(); + if (!Value.isInvalid) { + // Turn the expression into a stmt. + StmtResult R = Actions.ParseExprStmt(Value.Val); + if (!R.isInvalid) + ThirdPart = R.Val; + } + } + + // Match the ')'. + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + + // Read the body statement. + StmtResult Body = ParseStatement(); + + // Leave the for-scope. + ExitScope(); + + if (Body.isInvalid) + return Body; + + return Actions.ParseForStmt(ForLoc, LParenLoc, FirstPart, SecondPart, + ThirdPart, RParenLoc, Body.Val); +} + +/// ParseGotoStatement +/// jump-statement: +/// 'goto' identifier ';' +/// [GNU] 'goto' '*' expression ';' +/// +/// Note: this lets the caller parse the end ';'. +/// +Parser::StmtResult Parser::ParseGotoStatement() { + assert(Tok.getKind() == tok::kw_goto && "Not a goto stmt!"); + SourceLocation GotoLoc = ConsumeToken(); // eat the 'goto'. + + StmtResult Res; + if (Tok.getKind() == tok::identifier) { + Res = Actions.ParseGotoStmt(GotoLoc, Tok.getLocation(), + Tok.getIdentifierInfo()); + ConsumeToken(); + } else if (Tok.getKind() == tok::star && !getLang().NoExtensions) { + // GNU indirect goto extension. + Diag(Tok, diag::ext_gnu_indirect_goto); + SourceLocation StarLoc = ConsumeToken(); + ExprResult R = ParseExpression(); + if (R.isInvalid) { // Skip to the semicolon, but don't consume it. + SkipUntil(tok::semi, false, true); + return true; + } + Res = Actions.ParseIndirectGotoStmt(GotoLoc, StarLoc, R.Val); + } + return Res; +} + +/// ParseContinueStatement +/// jump-statement: +/// 'continue' ';' +/// +/// Note: this lets the caller parse the end ';'. +/// +Parser::StmtResult Parser::ParseContinueStatement() { + SourceLocation ContinueLoc = ConsumeToken(); // eat the 'continue'. + return Actions.ParseContinueStmt(ContinueLoc, CurScope); +} + +/// ParseBreakStatement +/// jump-statement: +/// 'break' ';' +/// +/// Note: this lets the caller parse the end ';'. +/// +Parser::StmtResult Parser::ParseBreakStatement() { + SourceLocation BreakLoc = ConsumeToken(); // eat the 'break'. + return Actions.ParseBreakStmt(BreakLoc, CurScope); +} + +/// ParseReturnStatement +/// jump-statement: +/// 'return' expression[opt] ';' +Parser::StmtResult Parser::ParseReturnStatement() { + assert(Tok.getKind() == tok::kw_return && "Not a return stmt!"); + SourceLocation ReturnLoc = ConsumeToken(); // eat the 'return'. + + ExprResult R(0); + if (Tok.getKind() != tok::semi) { + R = ParseExpression(); + if (R.isInvalid) { // Skip to the semicolon, but don't consume it. + SkipUntil(tok::semi, false, true); + return true; + } + } + return Actions.ParseReturnStmt(ReturnLoc, R.Val); +} + +/// ParseAsmStatement - Parse a GNU extended asm statement. +/// [GNU] asm-statement: +/// 'asm' type-qualifier[opt] '(' asm-argument ')' ';' +/// +/// [GNU] asm-argument: +/// asm-string-literal +/// asm-string-literal ':' asm-operands[opt] +/// asm-string-literal ':' asm-operands[opt] ':' asm-operands[opt] +/// asm-string-literal ':' asm-operands[opt] ':' asm-operands[opt] +/// ':' asm-clobbers +/// +/// [GNU] asm-clobbers: +/// asm-string-literal +/// asm-clobbers ',' asm-string-literal +/// +Parser::StmtResult Parser::ParseAsmStatement() { + assert(Tok.getKind() == tok::kw_asm && "Not an asm stmt"); + ConsumeToken(); + + DeclSpec DS; + SourceLocation Loc = Tok.getLocation(); + ParseTypeQualifierListOpt(DS); + + // GNU asms accept, but warn, about type-qualifiers other than volatile. + if (DS.getTypeQualifiers() & DeclSpec::TQ_const) + Diag(Loc, diag::w_asm_qualifier_ignored, "const"); + if (DS.getTypeQualifiers() & DeclSpec::TQ_restrict) + Diag(Loc, diag::w_asm_qualifier_ignored, "restrict"); + + // Remember if this was a volatile asm. + //bool isVolatile = DS.TypeQualifiers & DeclSpec::TQ_volatile; + + if (Tok.getKind() != tok::l_paren) { + Diag(Tok, diag::err_expected_lparen_after, "asm"); + SkipUntil(tok::r_paren); + return true; + } + Loc = ConsumeParen(); + + ParseAsmStringLiteral(); + + // Parse Outputs, if present. + ParseAsmOperandsOpt(); + + // Parse Inputs, if present. + ParseAsmOperandsOpt(); + + // Parse the clobbers, if present. + if (Tok.getKind() == tok::colon) { + ConsumeToken(); + + if (isTokenStringLiteral()) { + // Parse the asm-string list for clobbers. + while (1) { + ParseAsmStringLiteral(); + + if (Tok.getKind() != tok::comma) break; + ConsumeToken(); + } + } + } + + MatchRHSPunctuation(tok::r_paren, Loc); + + // FIXME: Implement action for asm parsing. + return false; +} + +/// ParseAsmOperands - Parse the asm-operands production as used by +/// asm-statement. We also parse a leading ':' token. If the leading colon is +/// not present, we do not parse anything. +/// +/// [GNU] asm-operands: +/// asm-operand +/// asm-operands ',' asm-operand +/// +/// [GNU] asm-operand: +/// asm-string-literal '(' expression ')' +/// '[' identifier ']' asm-string-literal '(' expression ')' +/// +void Parser::ParseAsmOperandsOpt() { + // Only do anything if this operand is present. + if (Tok.getKind() != tok::colon) return; + ConsumeToken(); + + // 'asm-operands' isn't present? + if (!isTokenStringLiteral() && Tok.getKind() != tok::l_square) + return; + + while (1) { + // Read the [id] if present. + if (Tok.getKind() == tok::l_square) { + SourceLocation Loc = ConsumeBracket(); + + if (Tok.getKind() != tok::identifier) { + Diag(Tok, diag::err_expected_ident); + SkipUntil(tok::r_paren); + return; + } + MatchRHSPunctuation(tok::r_square, Loc); + } + + ParseAsmStringLiteral(); + + if (Tok.getKind() != tok::l_paren) { + Diag(Tok, diag::err_expected_lparen_after, "asm operand"); + SkipUntil(tok::r_paren); + return; + } + + // Read the parenthesized expression. + ExprResult Res = ParseSimpleParenExpression(); + if (Res.isInvalid) { + SkipUntil(tok::r_paren); + return; + } + + // Eat the comma and continue parsing if it exists. + if (Tok.getKind() != tok::comma) return; + ConsumeToken(); + } +} |