//===- AsmLexer.cpp - Lexer for Assembly Files ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This class implements the lexer for assembly files. // //===----------------------------------------------------------------------===// #include "llvm/MC/MCParser/AsmLexer.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/SaveAndRestore.h" #include #include #include #include #include #include #include using namespace llvm; AsmLexer::AsmLexer(const MCAsmInfo &MAI) : MAI(MAI) { AllowAtInIdentifier = !StringRef(MAI.getCommentString()).startswith("@"); LexMotorolaIntegers = MAI.shouldUseMotorolaIntegers(); } AsmLexer::~AsmLexer() = default; void AsmLexer::setBuffer(StringRef Buf, const char *ptr, bool EndStatementAtEOF) { CurBuf = Buf; if (ptr) CurPtr = ptr; else CurPtr = CurBuf.begin(); TokStart = nullptr; this->EndStatementAtEOF = EndStatementAtEOF; } /// ReturnError - Set the error to the specified string at the specified /// location. This is defined to always return AsmToken::Error. AsmToken AsmLexer::ReturnError(const char *Loc, const std::string &Msg) { SetError(SMLoc::getFromPointer(Loc), Msg); return AsmToken(AsmToken::Error, StringRef(Loc, CurPtr - Loc)); } int AsmLexer::getNextChar() { if (CurPtr == CurBuf.end()) return EOF; return (unsigned char)*CurPtr++; } int AsmLexer::peekNextChar() { if (CurPtr == CurBuf.end()) return EOF; return (unsigned char)*CurPtr; } /// The leading integral digit sequence and dot should have already been /// consumed, some or all of the fractional digit sequence *can* have been /// consumed. AsmToken AsmLexer::LexFloatLiteral() { // Skip the fractional digit sequence. while (isDigit(*CurPtr)) ++CurPtr; if (*CurPtr == '-' || *CurPtr == '+') return ReturnError(CurPtr, "invalid sign in float literal"); // Check for exponent if ((*CurPtr == 'e' || *CurPtr == 'E')) { ++CurPtr; if (*CurPtr == '-' || *CurPtr == '+') ++CurPtr; while (isDigit(*CurPtr)) ++CurPtr; } return AsmToken(AsmToken::Real, StringRef(TokStart, CurPtr - TokStart)); } /// LexHexFloatLiteral matches essentially (.[0-9a-fA-F]*)?[pP][+-]?[0-9a-fA-F]+ /// while making sure there are enough actual digits around for the constant to /// be valid. /// /// The leading "0x[0-9a-fA-F]*" (i.e. integer part) has already been consumed /// before we get here. AsmToken AsmLexer::LexHexFloatLiteral(bool NoIntDigits) { assert((*CurPtr == 'p' || *CurPtr == 'P' || *CurPtr == '.') && "unexpected parse state in floating hex"); bool NoFracDigits = true; // Skip the fractional part if there is one if (*CurPtr == '.') { ++CurPtr; const char *FracStart = CurPtr; while (isHexDigit(*CurPtr)) ++CurPtr; NoFracDigits = CurPtr == FracStart; } if (NoIntDigits && NoFracDigits) return ReturnError(TokStart, "invalid hexadecimal floating-point constant: " "expected at least one significand digit"); // Make sure we do have some kind of proper exponent part if (*CurPtr != 'p' && *CurPtr != 'P') return ReturnError(TokStart, "invalid hexadecimal floating-point constant: " "expected exponent part 'p'"); ++CurPtr; if (*CurPtr == '+' || *CurPtr == '-') ++CurPtr; // N.b. exponent digits are *not* hex const char *ExpStart = CurPtr; while (isDigit(*CurPtr)) ++CurPtr; if (CurPtr == ExpStart) return ReturnError(TokStart, "invalid hexadecimal floating-point constant: " "expected at least one exponent digit"); return AsmToken(AsmToken::Real, StringRef(TokStart, CurPtr - TokStart)); } /// LexIdentifier: [a-zA-Z_$.@?][a-zA-Z0-9_$.@#?]* static bool isIdentifierChar(char C, bool AllowAt, bool AllowHash) { return isAlnum(C) || C == '_' || C == '$' || C == '.' || C == '?' || (AllowAt && C == '@') || (AllowHash && C == '#'); } AsmToken AsmLexer::LexIdentifier() { // Check for floating point literals. if (CurPtr[-1] == '.' && isDigit(*CurPtr)) { // Disambiguate a .1243foo identifier from a floating literal. while (isDigit(*CurPtr)) ++CurPtr; if (!isIdentifierChar(*CurPtr, AllowAtInIdentifier, AllowHashInIdentifier) || *CurPtr == 'e' || *CurPtr == 'E') return LexFloatLiteral(); } while (isIdentifierChar(*CurPtr, AllowAtInIdentifier, AllowHashInIdentifier)) ++CurPtr; // Handle . as a special case. if (CurPtr == TokStart+1 && TokStart[0] == '.') return AsmToken(AsmToken::Dot, StringRef(TokStart, 1)); return AsmToken(AsmToken::Identifier, StringRef(TokStart, CurPtr - TokStart)); } /// LexSlash: Slash: / /// C-Style Comment: /* ... */ /// C-style Comment: // ... AsmToken AsmLexer::LexSlash() { if (!MAI.shouldAllowAdditionalComments()) { IsAtStartOfStatement = false; return AsmToken(AsmToken::Slash, StringRef(TokStart, 1)); } switch (*CurPtr) { case '*': IsAtStartOfStatement = false; break; // C style comment. case '/': ++CurPtr; return LexLineComment(); default: IsAtStartOfStatement = false; return AsmToken(AsmToken::Slash, StringRef(TokStart, 1)); } // C Style comment. ++CurPtr; // skip the star. const char *CommentTextStart = CurPtr; while (CurPtr != CurBuf.end()) { switch (*CurPtr++) { case '*': // End of the comment? if (*CurPtr != '/') break; // If we have a CommentConsumer, notify it about the comment. if (CommentConsumer) { CommentConsumer->HandleComment( SMLoc::getFromPointer(CommentTextStart), StringRef(CommentTextStart, CurPtr - 1 - CommentTextStart)); } ++CurPtr; // End the */. return AsmToken(AsmToken::Comment, StringRef(TokStart, CurPtr - TokStart)); } } return ReturnError(TokStart, "unterminated comment"); } /// LexLineComment: Comment: #[^\n]* /// : //[^\n]* AsmToken AsmLexer::LexLineComment() { // Mark This as an end of statement with a body of the // comment. While it would be nicer to leave this two tokens, // backwards compatability with TargetParsers makes keeping this in this form // better. const char *CommentTextStart = CurPtr; int CurChar = getNextChar(); while (CurChar != '\n' && CurChar != '\r' && CurChar != EOF) CurChar = getNextChar(); const char *NewlinePtr = CurPtr; if (CurChar == '\r' && CurPtr != CurBuf.end() && *CurPtr == '\n') ++CurPtr; // If we have a CommentConsumer, notify it about the comment. if (CommentConsumer) { CommentConsumer->HandleComment( SMLoc::getFromPointer(CommentTextStart), StringRef(CommentTextStart, NewlinePtr - 1 - CommentTextStart)); } IsAtStartOfLine = true; // This is a whole line comment. leave newline if (IsAtStartOfStatement) return AsmToken(AsmToken::EndOfStatement, StringRef(TokStart, CurPtr - TokStart)); IsAtStartOfStatement = true; return AsmToken(AsmToken::EndOfStatement, StringRef(TokStart, CurPtr - 1 - TokStart)); } static void SkipIgnoredIntegerSuffix(const char *&CurPtr) { // Skip case-insensitive ULL, UL, U, L and LL suffixes. if (CurPtr[0] == 'U' || CurPtr[0] == 'u') ++CurPtr; if (CurPtr[0] == 'L' || CurPtr[0] == 'l') ++CurPtr; if (CurPtr[0] == 'L' || CurPtr[0] == 'l') ++CurPtr; } // Look ahead to search for first non-hex digit, if it's [hH], then we treat the // integer as a hexadecimal, possibly with leading zeroes. static unsigned doHexLookAhead(const char *&CurPtr, unsigned DefaultRadix, bool LexHex) { const char *FirstNonDec = nullptr; const char *LookAhead = CurPtr; while (true) { if (isDigit(*LookAhead)) { ++LookAhead; } else { if (!FirstNonDec) FirstNonDec = LookAhead; // Keep going if we are looking for a 'h' suffix. if (LexHex && isHexDigit(*LookAhead)) ++LookAhead; else break; } } bool isHex = LexHex && (*LookAhead == 'h' || *LookAhead == 'H'); CurPtr = isHex || !FirstNonDec ? LookAhead : FirstNonDec; if (isHex) return 16; return DefaultRadix; } static const char *findLastDigit(const char *CurPtr, unsigned DefaultRadix) { while (hexDigitValue(*CurPtr) < DefaultRadix) { ++CurPtr; } return CurPtr; } static AsmToken intToken(StringRef Ref, APInt &Value) { if (Value.isIntN(64)) return AsmToken(AsmToken::Integer, Ref, Value); return AsmToken(AsmToken::BigNum, Ref, Value); } static std::string radixName(unsigned Radix) { switch (Radix) { case 2: return "binary"; case 8: return "octal"; case 10: return "decimal"; case 16: return "hexadecimal"; default: return "base-" + std::to_string(Radix); } } /// LexDigit: First character is [0-9]. /// Local Label: [0-9][:] /// Forward/Backward Label: [0-9][fb] /// Binary integer: 0b[01]+ /// Octal integer: 0[0-7]+ /// Hex integer: 0x[0-9a-fA-F]+ or [0x]?[0-9][0-9a-fA-F]*[hH] /// Decimal integer: [1-9][0-9]* AsmToken AsmLexer::LexDigit() { // MASM-flavor binary integer: [01]+[yY] (if DefaultRadix < 16, [bByY]) // MASM-flavor octal integer: [0-7]+[oOqQ] // MASM-flavor decimal integer: [0-9]+[tT] (if DefaultRadix < 16, [dDtT]) // MASM-flavor hexadecimal integer: [0-9][0-9a-fA-F]*[hH] if (LexMasmIntegers && isdigit(CurPtr[-1])) { const char *FirstNonBinary = (CurPtr[-1] != '0' && CurPtr[-1] != '1') ? CurPtr - 1 : nullptr; const char *FirstNonDecimal = (CurPtr[-1] < '0' || CurPtr[-1] > '9') ? CurPtr - 1 : nullptr; const char *OldCurPtr = CurPtr; while (isHexDigit(*CurPtr)) { switch (*CurPtr) { default: if (!FirstNonDecimal) { FirstNonDecimal = CurPtr; } [[fallthrough]]; case '9': case '8': case '7': case '6': case '5': case '4': case '3': case '2': if (!FirstNonBinary) { FirstNonBinary = CurPtr; } break; case '1': case '0': break; } ++CurPtr; } if (*CurPtr == '.') { // MASM float literals (other than hex floats) always contain a ".", and // are always written in decimal. ++CurPtr; return LexFloatLiteral(); } if (LexMasmHexFloats && (*CurPtr == 'r' || *CurPtr == 'R')) { ++CurPtr; return AsmToken(AsmToken::Real, StringRef(TokStart, CurPtr - TokStart)); } unsigned Radix = 0; if (*CurPtr == 'h' || *CurPtr == 'H') { // hexadecimal number ++CurPtr; Radix = 16; } else if (*CurPtr == 't' || *CurPtr == 'T') { // decimal number ++CurPtr; Radix = 10; } else if (*CurPtr == 'o' || *CurPtr == 'O' || *CurPtr == 'q' || *CurPtr == 'Q') { // octal number ++CurPtr; Radix = 8; } else if (*CurPtr == 'y' || *CurPtr == 'Y') { // binary number ++CurPtr; Radix = 2; } else if (FirstNonDecimal && FirstNonDecimal + 1 == CurPtr && DefaultRadix < 14 && (*FirstNonDecimal == 'd' || *FirstNonDecimal == 'D')) { Radix = 10; } else if (FirstNonBinary && FirstNonBinary + 1 == CurPtr && DefaultRadix < 12 && (*FirstNonBinary == 'b' || *FirstNonBinary == 'B')) { Radix = 2; } if (Radix) { StringRef Result(TokStart, CurPtr - TokStart); APInt Value(128, 0, true); if (Result.drop_back().getAsInteger(Radix, Value)) return ReturnError(TokStart, "invalid " + radixName(Radix) + " number"); // MSVC accepts and ignores type suffices on integer literals. SkipIgnoredIntegerSuffix(CurPtr); return intToken(Result, Value); } // default-radix integers, or floating point numbers, fall through CurPtr = OldCurPtr; } // MASM default-radix integers: [0-9a-fA-F]+ // (All other integer literals have a radix specifier.) if (LexMasmIntegers && UseMasmDefaultRadix) { CurPtr = findLastDigit(CurPtr, 16); StringRef Result(TokStart, CurPtr - TokStart); APInt Value(128, 0, true); if (Result.getAsInteger(DefaultRadix, Value)) { return ReturnError(TokStart, "invalid " + radixName(DefaultRadix) + " number"); } return intToken(Result, Value); } // Motorola hex integers: $[0-9a-fA-F]+ if (LexMotorolaIntegers && CurPtr[-1] == '$') { const char *NumStart = CurPtr; while (isHexDigit(CurPtr[0])) ++CurPtr; APInt Result(128, 0); if (StringRef(NumStart, CurPtr - NumStart).getAsInteger(16, Result)) return ReturnError(TokStart, "invalid hexadecimal number"); return intToken(StringRef(TokStart, CurPtr - TokStart), Result); } // Motorola binary integers: %[01]+ if (LexMotorolaIntegers && CurPtr[-1] == '%') { const char *NumStart = CurPtr; while (*CurPtr == '0' || *CurPtr == '1') ++CurPtr; APInt Result(128, 0); if (StringRef(NumStart, CurPtr - NumStart).getAsInteger(2, Result)) return ReturnError(TokStart, "invalid binary number"); return intToken(StringRef(TokStart, CurPtr - TokStart), Result); } // Decimal integer: [1-9][0-9]* // HLASM-flavour decimal integer: [0-9][0-9]* // FIXME: Later on, support for fb for HLASM has to be added in // as they probably would be needed for asm goto if (LexHLASMIntegers || CurPtr[-1] != '0' || CurPtr[0] == '.') { unsigned Radix = doHexLookAhead(CurPtr, 10, LexMasmIntegers); if (!LexHLASMIntegers) { bool IsHex = Radix == 16; // Check for floating point literals. if (!IsHex && (*CurPtr == '.' || *CurPtr == 'e' || *CurPtr == 'E')) { if (*CurPtr == '.') ++CurPtr; return LexFloatLiteral(); } } StringRef Result(TokStart, CurPtr - TokStart); APInt Value(128, 0, true); if (Result.getAsInteger(Radix, Value)) return ReturnError(TokStart, "invalid " + radixName(Radix) + " number"); if (!LexHLASMIntegers) // The darwin/x86 (and x86-64) assembler accepts and ignores type // suffices on integer literals. SkipIgnoredIntegerSuffix(CurPtr); return intToken(Result, Value); } if (!LexMasmIntegers && ((*CurPtr == 'b') || (*CurPtr == 'B'))) { ++CurPtr; // See if we actually have "0b" as part of something like "jmp 0b\n" if (!isDigit(CurPtr[0])) { --CurPtr; StringRef Result(TokStart, CurPtr - TokStart); return AsmToken(AsmToken::Integer, Result, 0); } const char *NumStart = CurPtr; while (CurPtr[0] == '0' || CurPtr[0] == '1') ++CurPtr; // Requires at least one binary digit. if (CurPtr == NumStart) return ReturnError(TokStart, "invalid binary number"); StringRef Result(TokStart, CurPtr - TokStart); APInt Value(128, 0, true); if (Result.substr(2).getAsInteger(2, Value)) return ReturnError(TokStart, "invalid binary number"); // The darwin/x86 (and x86-64) assembler accepts and ignores ULL and LL // suffixes on integer literals. SkipIgnoredIntegerSuffix(CurPtr); return intToken(Result, Value); } if ((*CurPtr == 'x') || (*CurPtr == 'X')) { ++CurPtr; const char *NumStart = CurPtr; while (isHexDigit(CurPtr[0])) ++CurPtr; // "0x.0p0" is valid, and "0x0p0" (but not "0xp0" for example, which will be // diagnosed by LexHexFloatLiteral). if (CurPtr[0] == '.' || CurPtr[0] == 'p' || CurPtr[0] == 'P') return LexHexFloatLiteral(NumStart == CurPtr); // Otherwise requires at least one hex digit. if (CurPtr == NumStart) return ReturnError(CurPtr-2, "invalid hexadecimal number"); APInt Result(128, 0); if (StringRef(TokStart, CurPtr - TokStart).getAsInteger(0, Result)) return ReturnError(TokStart, "invalid hexadecimal number"); // Consume the optional [hH]. if (LexMasmIntegers && (*CurPtr == 'h' || *CurPtr == 'H')) ++CurPtr; // The darwin/x86 (and x86-64) assembler accepts and ignores ULL and LL // suffixes on integer literals. SkipIgnoredIntegerSuffix(CurPtr); return intToken(StringRef(TokStart, CurPtr - TokStart), Result); } // Either octal or hexadecimal. APInt Value(128, 0, true); unsigned Radix = doHexLookAhead(CurPtr, 8, LexMasmIntegers); StringRef Result(TokStart, CurPtr - TokStart); if (Result.getAsInteger(Radix, Value)) return ReturnError(TokStart, "invalid " + radixName(Radix) + " number"); // Consume the [hH]. if (Radix == 16) ++CurPtr; // The darwin/x86 (and x86-64) assembler accepts and ignores ULL and LL // suffixes on integer literals. SkipIgnoredIntegerSuffix(CurPtr); return intToken(Result, Value); } /// LexSingleQuote: Integer: 'b' AsmToken AsmLexer::LexSingleQuote() { int CurChar = getNextChar(); if (LexHLASMStrings) return ReturnError(TokStart, "invalid usage of character literals"); if (LexMasmStrings) { while (CurChar != EOF) { if (CurChar != '\'') { CurChar = getNextChar(); } else if (peekNextChar() == '\'') { // In MASM single-quote strings, doubled single-quotes mean an escaped // single quote, so should be lexed in. getNextChar(); CurChar = getNextChar(); } else { break; } } if (CurChar == EOF) return ReturnError(TokStart, "unterminated string constant"); return AsmToken(AsmToken::String, StringRef(TokStart, CurPtr - TokStart)); } if (CurChar == '\\') CurChar = getNextChar(); if (CurChar == EOF) return ReturnError(TokStart, "unterminated single quote"); CurChar = getNextChar(); if (CurChar != '\'') return ReturnError(TokStart, "single quote way too long"); // The idea here being that 'c' is basically just an integral // constant. StringRef Res = StringRef(TokStart,CurPtr - TokStart); long long Value; if (Res.startswith("\'\\")) { char theChar = Res[2]; switch (theChar) { default: Value = theChar; break; case '\'': Value = '\''; break; case 't': Value = '\t'; break; case 'n': Value = '\n'; break; case 'b': Value = '\b'; break; case 'f': Value = '\f'; break; case 'r': Value = '\r'; break; } } else Value = TokStart[1]; return AsmToken(AsmToken::Integer, Res, Value); } /// LexQuote: String: "..." AsmToken AsmLexer::LexQuote() { int CurChar = getNextChar(); if (LexHLASMStrings) return ReturnError(TokStart, "invalid usage of string literals"); if (LexMasmStrings) { while (CurChar != EOF) { if (CurChar != '"') { CurChar = getNextChar(); } else if (peekNextChar() == '"') { // In MASM double-quoted strings, doubled double-quotes mean an escaped // double quote, so should be lexed in. getNextChar(); CurChar = getNextChar(); } else { break; } } if (CurChar == EOF) return ReturnError(TokStart, "unterminated string constant"); return AsmToken(AsmToken::String, StringRef(TokStart, CurPtr - TokStart)); } // TODO: does gas allow multiline string constants? while (CurChar != '"') { if (CurChar == '\\') { // Allow \", etc. CurChar = getNextChar(); } if (CurChar == EOF) return ReturnError(TokStart, "unterminated string constant"); CurChar = getNextChar(); } return AsmToken(AsmToken::String, StringRef(TokStart, CurPtr - TokStart)); } StringRef AsmLexer::LexUntilEndOfStatement() { TokStart = CurPtr; while (!isAtStartOfComment(CurPtr) && // Start of line comment. !isAtStatementSeparator(CurPtr) && // End of statement marker. *CurPtr != '\n' && *CurPtr != '\r' && CurPtr != CurBuf.end()) { ++CurPtr; } return StringRef(TokStart, CurPtr-TokStart); } StringRef AsmLexer::LexUntilEndOfLine() { TokStart = CurPtr; while (*CurPtr != '\n' && *CurPtr != '\r' && CurPtr != CurBuf.end()) { ++CurPtr; } return StringRef(TokStart, CurPtr-TokStart); } size_t AsmLexer::peekTokens(MutableArrayRef Buf, bool ShouldSkipSpace) { SaveAndRestore SavedTokenStart(TokStart); SaveAndRestore SavedCurPtr(CurPtr); SaveAndRestore SavedAtStartOfLine(IsAtStartOfLine); SaveAndRestore SavedAtStartOfStatement(IsAtStartOfStatement); SaveAndRestore SavedSkipSpace(SkipSpace, ShouldSkipSpace); SaveAndRestore SavedIsPeeking(IsPeeking, true); std::string SavedErr = getErr(); SMLoc SavedErrLoc = getErrLoc(); size_t ReadCount; for (ReadCount = 0; ReadCount < Buf.size(); ++ReadCount) { AsmToken Token = LexToken(); Buf[ReadCount] = Token; if (Token.is(AsmToken::Eof)) break; } SetError(SavedErrLoc, SavedErr); return ReadCount; } bool AsmLexer::isAtStartOfComment(const char *Ptr) { if (MAI.getRestrictCommentStringToStartOfStatement() && !IsAtStartOfStatement) return false; StringRef CommentString = MAI.getCommentString(); if (CommentString.size() == 1) return CommentString[0] == Ptr[0]; // Allow # preprocessor comments also be counted as comments for "##" cases if (CommentString[1] == '#') return CommentString[0] == Ptr[0]; return strncmp(Ptr, CommentString.data(), CommentString.size()) == 0; } bool AsmLexer::isAtStatementSeparator(const char *Ptr) { return strncmp(Ptr, MAI.getSeparatorString(), strlen(MAI.getSeparatorString())) == 0; } AsmToken AsmLexer::LexToken() { TokStart = CurPtr; // This always consumes at least one character. int CurChar = getNextChar(); if (!IsPeeking && CurChar == '#' && IsAtStartOfStatement) { // If this starts with a '#', this may be a cpp // hash directive and otherwise a line comment. AsmToken TokenBuf[2]; MutableArrayRef Buf(TokenBuf, 2); size_t num = peekTokens(Buf, true); // There cannot be a space preceding this if (IsAtStartOfLine && num == 2 && TokenBuf[0].is(AsmToken::Integer) && TokenBuf[1].is(AsmToken::String)) { CurPtr = TokStart; // reset curPtr; StringRef s = LexUntilEndOfLine(); UnLex(TokenBuf[1]); UnLex(TokenBuf[0]); return AsmToken(AsmToken::HashDirective, s); } if (MAI.shouldAllowAdditionalComments()) return LexLineComment(); } if (isAtStartOfComment(TokStart)) return LexLineComment(); if (isAtStatementSeparator(TokStart)) { CurPtr += strlen(MAI.getSeparatorString()) - 1; IsAtStartOfLine = true; IsAtStartOfStatement = true; return AsmToken(AsmToken::EndOfStatement, StringRef(TokStart, strlen(MAI.getSeparatorString()))); } // If we're missing a newline at EOF, make sure we still get an // EndOfStatement token before the Eof token. if (CurChar == EOF && !IsAtStartOfStatement && EndStatementAtEOF) { IsAtStartOfLine = true; IsAtStartOfStatement = true; return AsmToken(AsmToken::EndOfStatement, StringRef(TokStart, 0)); } IsAtStartOfLine = false; bool OldIsAtStartOfStatement = IsAtStartOfStatement; IsAtStartOfStatement = false; switch (CurChar) { default: // Handle identifier: [a-zA-Z_.?][a-zA-Z0-9_$.@#?]* if (isalpha(CurChar) || CurChar == '_' || CurChar == '.' || (MAI.doesAllowQuestionAtStartOfIdentifier() && CurChar == '?')) return LexIdentifier(); // Unknown character, emit an error. return ReturnError(TokStart, "invalid character in input"); case EOF: if (EndStatementAtEOF) { IsAtStartOfLine = true; IsAtStartOfStatement = true; } return AsmToken(AsmToken::Eof, StringRef(TokStart, 0)); case 0: case ' ': case '\t': IsAtStartOfStatement = OldIsAtStartOfStatement; while (*CurPtr == ' ' || *CurPtr == '\t') CurPtr++; if (SkipSpace) return LexToken(); // Ignore whitespace. else return AsmToken(AsmToken::Space, StringRef(TokStart, CurPtr - TokStart)); case '\r': { IsAtStartOfLine = true; IsAtStartOfStatement = true; // If this is a CR followed by LF, treat that as one token. if (CurPtr != CurBuf.end() && *CurPtr == '\n') ++CurPtr; return AsmToken(AsmToken::EndOfStatement, StringRef(TokStart, CurPtr - TokStart)); } case '\n': IsAtStartOfLine = true; IsAtStartOfStatement = true; return AsmToken(AsmToken::EndOfStatement, StringRef(TokStart, 1)); case ':': return AsmToken(AsmToken::Colon, StringRef(TokStart, 1)); case '+': return AsmToken(AsmToken::Plus, StringRef(TokStart, 1)); case '~': return AsmToken(AsmToken::Tilde, StringRef(TokStart, 1)); case '(': return AsmToken(AsmToken::LParen, StringRef(TokStart, 1)); case ')': return AsmToken(AsmToken::RParen, StringRef(TokStart, 1)); case '[': return AsmToken(AsmToken::LBrac, StringRef(TokStart, 1)); case ']': return AsmToken(AsmToken::RBrac, StringRef(TokStart, 1)); case '{': return AsmToken(AsmToken::LCurly, StringRef(TokStart, 1)); case '}': return AsmToken(AsmToken::RCurly, StringRef(TokStart, 1)); case '*': return AsmToken(AsmToken::Star, StringRef(TokStart, 1)); case ',': return AsmToken(AsmToken::Comma, StringRef(TokStart, 1)); case '$': { if (LexMotorolaIntegers && isHexDigit(*CurPtr)) return LexDigit(); if (MAI.doesAllowDollarAtStartOfIdentifier()) return LexIdentifier(); return AsmToken(AsmToken::Dollar, StringRef(TokStart, 1)); } case '@': { if (MAI.doesAllowAtAtStartOfIdentifier()) return LexIdentifier(); return AsmToken(AsmToken::At, StringRef(TokStart, 1)); } case '\\': return AsmToken(AsmToken::BackSlash, StringRef(TokStart, 1)); case '=': if (*CurPtr == '=') { ++CurPtr; return AsmToken(AsmToken::EqualEqual, StringRef(TokStart, 2)); } return AsmToken(AsmToken::Equal, StringRef(TokStart, 1)); case '-': if (*CurPtr == '>') { ++CurPtr; return AsmToken(AsmToken::MinusGreater, StringRef(TokStart, 2)); } return AsmToken(AsmToken::Minus, StringRef(TokStart, 1)); case '|': if (*CurPtr == '|') { ++CurPtr; return AsmToken(AsmToken::PipePipe, StringRef(TokStart, 2)); } return AsmToken(AsmToken::Pipe, StringRef(TokStart, 1)); case '^': return AsmToken(AsmToken::Caret, StringRef(TokStart, 1)); case '&': if (*CurPtr == '&') { ++CurPtr; return AsmToken(AsmToken::AmpAmp, StringRef(TokStart, 2)); } return AsmToken(AsmToken::Amp, StringRef(TokStart, 1)); case '!': if (*CurPtr == '=') { ++CurPtr; return AsmToken(AsmToken::ExclaimEqual, StringRef(TokStart, 2)); } return AsmToken(AsmToken::Exclaim, StringRef(TokStart, 1)); case '%': if (LexMotorolaIntegers && (*CurPtr == '0' || *CurPtr == '1')) { return LexDigit(); } if (MAI.hasMipsExpressions()) { AsmToken::TokenKind Operator; unsigned OperatorLength; std::tie(Operator, OperatorLength) = StringSwitch>( StringRef(CurPtr)) .StartsWith("call16", {AsmToken::PercentCall16, 7}) .StartsWith("call_hi", {AsmToken::PercentCall_Hi, 8}) .StartsWith("call_lo", {AsmToken::PercentCall_Lo, 8}) .StartsWith("dtprel_hi", {AsmToken::PercentDtprel_Hi, 10}) .StartsWith("dtprel_lo", {AsmToken::PercentDtprel_Lo, 10}) .StartsWith("got_disp", {AsmToken::PercentGot_Disp, 9}) .StartsWith("got_hi", {AsmToken::PercentGot_Hi, 7}) .StartsWith("got_lo", {AsmToken::PercentGot_Lo, 7}) .StartsWith("got_ofst", {AsmToken::PercentGot_Ofst, 9}) .StartsWith("got_page", {AsmToken::PercentGot_Page, 9}) .StartsWith("gottprel", {AsmToken::PercentGottprel, 9}) .StartsWith("got", {AsmToken::PercentGot, 4}) .StartsWith("gp_rel", {AsmToken::PercentGp_Rel, 7}) .StartsWith("higher", {AsmToken::PercentHigher, 7}) .StartsWith("highest", {AsmToken::PercentHighest, 8}) .StartsWith("hi", {AsmToken::PercentHi, 3}) .StartsWith("lo", {AsmToken::PercentLo, 3}) .StartsWith("neg", {AsmToken::PercentNeg, 4}) .StartsWith("pcrel_hi", {AsmToken::PercentPcrel_Hi, 9}) .StartsWith("pcrel_lo", {AsmToken::PercentPcrel_Lo, 9}) .StartsWith("tlsgd", {AsmToken::PercentTlsgd, 6}) .StartsWith("tlsldm", {AsmToken::PercentTlsldm, 7}) .StartsWith("tprel_hi", {AsmToken::PercentTprel_Hi, 9}) .StartsWith("tprel_lo", {AsmToken::PercentTprel_Lo, 9}) .Default({AsmToken::Percent, 1}); if (Operator != AsmToken::Percent) { CurPtr += OperatorLength - 1; return AsmToken(Operator, StringRef(TokStart, OperatorLength)); } } return AsmToken(AsmToken::Percent, StringRef(TokStart, 1)); case '/': IsAtStartOfStatement = OldIsAtStartOfStatement; return LexSlash(); case '#': { if (MAI.doesAllowHashAtStartOfIdentifier()) return LexIdentifier(); return AsmToken(AsmToken::Hash, StringRef(TokStart, 1)); } case '\'': return LexSingleQuote(); case '"': return LexQuote(); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return LexDigit(); case '<': switch (*CurPtr) { case '<': ++CurPtr; return AsmToken(AsmToken::LessLess, StringRef(TokStart, 2)); case '=': ++CurPtr; return AsmToken(AsmToken::LessEqual, StringRef(TokStart, 2)); case '>': ++CurPtr; return AsmToken(AsmToken::LessGreater, StringRef(TokStart, 2)); default: return AsmToken(AsmToken::Less, StringRef(TokStart, 1)); } case '>': switch (*CurPtr) { case '>': ++CurPtr; return AsmToken(AsmToken::GreaterGreater, StringRef(TokStart, 2)); case '=': ++CurPtr; return AsmToken(AsmToken::GreaterEqual, StringRef(TokStart, 2)); default: return AsmToken(AsmToken::Greater, StringRef(TokStart, 1)); } // TODO: Quoted identifiers (objc methods etc) // local labels: [0-9][:] // Forward/backward labels: [0-9][fb] // Integers, fp constants, character constants. } }