106f32e7eSjoerg //===--- CommentLexer.h - Lexer for structured comments ---------*- C++ -*-===// 206f32e7eSjoerg // 306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information. 506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 606f32e7eSjoerg // 706f32e7eSjoerg //===----------------------------------------------------------------------===// 806f32e7eSjoerg // 906f32e7eSjoerg // This file defines lexer for structured comments and supporting token class. 1006f32e7eSjoerg // 1106f32e7eSjoerg //===----------------------------------------------------------------------===// 1206f32e7eSjoerg 1306f32e7eSjoerg #ifndef LLVM_CLANG_AST_COMMENTLEXER_H 1406f32e7eSjoerg #define LLVM_CLANG_AST_COMMENTLEXER_H 1506f32e7eSjoerg 1606f32e7eSjoerg #include "clang/Basic/Diagnostic.h" 1706f32e7eSjoerg #include "clang/Basic/SourceManager.h" 1806f32e7eSjoerg #include "llvm/ADT/SmallString.h" 1906f32e7eSjoerg #include "llvm/ADT/StringRef.h" 2006f32e7eSjoerg #include "llvm/Support/Allocator.h" 2106f32e7eSjoerg #include "llvm/Support/raw_ostream.h" 2206f32e7eSjoerg 2306f32e7eSjoerg namespace clang { 2406f32e7eSjoerg namespace comments { 2506f32e7eSjoerg 2606f32e7eSjoerg class Lexer; 2706f32e7eSjoerg class TextTokenRetokenizer; 2806f32e7eSjoerg struct CommandInfo; 2906f32e7eSjoerg class CommandTraits; 3006f32e7eSjoerg 3106f32e7eSjoerg namespace tok { 3206f32e7eSjoerg enum TokenKind { 3306f32e7eSjoerg eof, 3406f32e7eSjoerg newline, 3506f32e7eSjoerg text, 3606f32e7eSjoerg unknown_command, // Command that does not have an ID. 3706f32e7eSjoerg backslash_command, // Command with an ID, that used backslash marker. 3806f32e7eSjoerg at_command, // Command with an ID, that used 'at' marker. 3906f32e7eSjoerg verbatim_block_begin, 4006f32e7eSjoerg verbatim_block_line, 4106f32e7eSjoerg verbatim_block_end, 4206f32e7eSjoerg verbatim_line_name, 4306f32e7eSjoerg verbatim_line_text, 4406f32e7eSjoerg html_start_tag, // <tag 4506f32e7eSjoerg html_ident, // attr 4606f32e7eSjoerg html_equals, // = 4706f32e7eSjoerg html_quoted_string, // "blah\"blah" or 'blah\'blah' 4806f32e7eSjoerg html_greater, // > 4906f32e7eSjoerg html_slash_greater, // /> 5006f32e7eSjoerg html_end_tag // </tag 5106f32e7eSjoerg }; 5206f32e7eSjoerg } // end namespace tok 5306f32e7eSjoerg 5406f32e7eSjoerg /// Comment token. 5506f32e7eSjoerg class Token { 5606f32e7eSjoerg friend class Lexer; 5706f32e7eSjoerg friend class TextTokenRetokenizer; 5806f32e7eSjoerg 5906f32e7eSjoerg /// The location of the token. 6006f32e7eSjoerg SourceLocation Loc; 6106f32e7eSjoerg 6206f32e7eSjoerg /// The actual kind of the token. 6306f32e7eSjoerg tok::TokenKind Kind; 6406f32e7eSjoerg 6506f32e7eSjoerg /// Integer value associated with a token. 6606f32e7eSjoerg /// 6706f32e7eSjoerg /// If the token is a known command, contains command ID and TextPtr is 6806f32e7eSjoerg /// unused (command spelling can be found with CommandTraits). Otherwise, 6906f32e7eSjoerg /// contains the length of the string that starts at TextPtr. 7006f32e7eSjoerg unsigned IntVal; 7106f32e7eSjoerg 72*13fbcb42Sjoerg /// Length of the token spelling in comment. Can be 0 for synthenized 73*13fbcb42Sjoerg /// tokens. 74*13fbcb42Sjoerg unsigned Length; 75*13fbcb42Sjoerg 76*13fbcb42Sjoerg /// Contains text value associated with a token. 77*13fbcb42Sjoerg const char *TextPtr; 78*13fbcb42Sjoerg 7906f32e7eSjoerg public: getLocation()8006f32e7eSjoerg SourceLocation getLocation() const LLVM_READONLY { return Loc; } setLocation(SourceLocation SL)8106f32e7eSjoerg void setLocation(SourceLocation SL) { Loc = SL; } 8206f32e7eSjoerg getEndLocation()8306f32e7eSjoerg SourceLocation getEndLocation() const LLVM_READONLY { 8406f32e7eSjoerg if (Length == 0 || Length == 1) 8506f32e7eSjoerg return Loc; 8606f32e7eSjoerg return Loc.getLocWithOffset(Length - 1); 8706f32e7eSjoerg } 8806f32e7eSjoerg getKind()8906f32e7eSjoerg tok::TokenKind getKind() const LLVM_READONLY { return Kind; } setKind(tok::TokenKind K)9006f32e7eSjoerg void setKind(tok::TokenKind K) { Kind = K; } 9106f32e7eSjoerg is(tok::TokenKind K)9206f32e7eSjoerg bool is(tok::TokenKind K) const LLVM_READONLY { return Kind == K; } isNot(tok::TokenKind K)9306f32e7eSjoerg bool isNot(tok::TokenKind K) const LLVM_READONLY { return Kind != K; } 9406f32e7eSjoerg getLength()9506f32e7eSjoerg unsigned getLength() const LLVM_READONLY { return Length; } setLength(unsigned L)9606f32e7eSjoerg void setLength(unsigned L) { Length = L; } 9706f32e7eSjoerg getText()9806f32e7eSjoerg StringRef getText() const LLVM_READONLY { 9906f32e7eSjoerg assert(is(tok::text)); 10006f32e7eSjoerg return StringRef(TextPtr, IntVal); 10106f32e7eSjoerg } 10206f32e7eSjoerg setText(StringRef Text)10306f32e7eSjoerg void setText(StringRef Text) { 10406f32e7eSjoerg assert(is(tok::text)); 10506f32e7eSjoerg TextPtr = Text.data(); 10606f32e7eSjoerg IntVal = Text.size(); 10706f32e7eSjoerg } 10806f32e7eSjoerg getUnknownCommandName()10906f32e7eSjoerg StringRef getUnknownCommandName() const LLVM_READONLY { 11006f32e7eSjoerg assert(is(tok::unknown_command)); 11106f32e7eSjoerg return StringRef(TextPtr, IntVal); 11206f32e7eSjoerg } 11306f32e7eSjoerg setUnknownCommandName(StringRef Name)11406f32e7eSjoerg void setUnknownCommandName(StringRef Name) { 11506f32e7eSjoerg assert(is(tok::unknown_command)); 11606f32e7eSjoerg TextPtr = Name.data(); 11706f32e7eSjoerg IntVal = Name.size(); 11806f32e7eSjoerg } 11906f32e7eSjoerg getCommandID()12006f32e7eSjoerg unsigned getCommandID() const LLVM_READONLY { 12106f32e7eSjoerg assert(is(tok::backslash_command) || is(tok::at_command)); 12206f32e7eSjoerg return IntVal; 12306f32e7eSjoerg } 12406f32e7eSjoerg setCommandID(unsigned ID)12506f32e7eSjoerg void setCommandID(unsigned ID) { 12606f32e7eSjoerg assert(is(tok::backslash_command) || is(tok::at_command)); 12706f32e7eSjoerg IntVal = ID; 12806f32e7eSjoerg } 12906f32e7eSjoerg getVerbatimBlockID()13006f32e7eSjoerg unsigned getVerbatimBlockID() const LLVM_READONLY { 13106f32e7eSjoerg assert(is(tok::verbatim_block_begin) || is(tok::verbatim_block_end)); 13206f32e7eSjoerg return IntVal; 13306f32e7eSjoerg } 13406f32e7eSjoerg setVerbatimBlockID(unsigned ID)13506f32e7eSjoerg void setVerbatimBlockID(unsigned ID) { 13606f32e7eSjoerg assert(is(tok::verbatim_block_begin) || is(tok::verbatim_block_end)); 13706f32e7eSjoerg IntVal = ID; 13806f32e7eSjoerg } 13906f32e7eSjoerg getVerbatimBlockText()14006f32e7eSjoerg StringRef getVerbatimBlockText() const LLVM_READONLY { 14106f32e7eSjoerg assert(is(tok::verbatim_block_line)); 14206f32e7eSjoerg return StringRef(TextPtr, IntVal); 14306f32e7eSjoerg } 14406f32e7eSjoerg setVerbatimBlockText(StringRef Text)14506f32e7eSjoerg void setVerbatimBlockText(StringRef Text) { 14606f32e7eSjoerg assert(is(tok::verbatim_block_line)); 14706f32e7eSjoerg TextPtr = Text.data(); 14806f32e7eSjoerg IntVal = Text.size(); 14906f32e7eSjoerg } 15006f32e7eSjoerg getVerbatimLineID()15106f32e7eSjoerg unsigned getVerbatimLineID() const LLVM_READONLY { 15206f32e7eSjoerg assert(is(tok::verbatim_line_name)); 15306f32e7eSjoerg return IntVal; 15406f32e7eSjoerg } 15506f32e7eSjoerg setVerbatimLineID(unsigned ID)15606f32e7eSjoerg void setVerbatimLineID(unsigned ID) { 15706f32e7eSjoerg assert(is(tok::verbatim_line_name)); 15806f32e7eSjoerg IntVal = ID; 15906f32e7eSjoerg } 16006f32e7eSjoerg getVerbatimLineText()16106f32e7eSjoerg StringRef getVerbatimLineText() const LLVM_READONLY { 16206f32e7eSjoerg assert(is(tok::verbatim_line_text)); 16306f32e7eSjoerg return StringRef(TextPtr, IntVal); 16406f32e7eSjoerg } 16506f32e7eSjoerg setVerbatimLineText(StringRef Text)16606f32e7eSjoerg void setVerbatimLineText(StringRef Text) { 16706f32e7eSjoerg assert(is(tok::verbatim_line_text)); 16806f32e7eSjoerg TextPtr = Text.data(); 16906f32e7eSjoerg IntVal = Text.size(); 17006f32e7eSjoerg } 17106f32e7eSjoerg getHTMLTagStartName()17206f32e7eSjoerg StringRef getHTMLTagStartName() const LLVM_READONLY { 17306f32e7eSjoerg assert(is(tok::html_start_tag)); 17406f32e7eSjoerg return StringRef(TextPtr, IntVal); 17506f32e7eSjoerg } 17606f32e7eSjoerg setHTMLTagStartName(StringRef Name)17706f32e7eSjoerg void setHTMLTagStartName(StringRef Name) { 17806f32e7eSjoerg assert(is(tok::html_start_tag)); 17906f32e7eSjoerg TextPtr = Name.data(); 18006f32e7eSjoerg IntVal = Name.size(); 18106f32e7eSjoerg } 18206f32e7eSjoerg getHTMLIdent()18306f32e7eSjoerg StringRef getHTMLIdent() const LLVM_READONLY { 18406f32e7eSjoerg assert(is(tok::html_ident)); 18506f32e7eSjoerg return StringRef(TextPtr, IntVal); 18606f32e7eSjoerg } 18706f32e7eSjoerg setHTMLIdent(StringRef Name)18806f32e7eSjoerg void setHTMLIdent(StringRef Name) { 18906f32e7eSjoerg assert(is(tok::html_ident)); 19006f32e7eSjoerg TextPtr = Name.data(); 19106f32e7eSjoerg IntVal = Name.size(); 19206f32e7eSjoerg } 19306f32e7eSjoerg getHTMLQuotedString()19406f32e7eSjoerg StringRef getHTMLQuotedString() const LLVM_READONLY { 19506f32e7eSjoerg assert(is(tok::html_quoted_string)); 19606f32e7eSjoerg return StringRef(TextPtr, IntVal); 19706f32e7eSjoerg } 19806f32e7eSjoerg setHTMLQuotedString(StringRef Str)19906f32e7eSjoerg void setHTMLQuotedString(StringRef Str) { 20006f32e7eSjoerg assert(is(tok::html_quoted_string)); 20106f32e7eSjoerg TextPtr = Str.data(); 20206f32e7eSjoerg IntVal = Str.size(); 20306f32e7eSjoerg } 20406f32e7eSjoerg getHTMLTagEndName()20506f32e7eSjoerg StringRef getHTMLTagEndName() const LLVM_READONLY { 20606f32e7eSjoerg assert(is(tok::html_end_tag)); 20706f32e7eSjoerg return StringRef(TextPtr, IntVal); 20806f32e7eSjoerg } 20906f32e7eSjoerg setHTMLTagEndName(StringRef Name)21006f32e7eSjoerg void setHTMLTagEndName(StringRef Name) { 21106f32e7eSjoerg assert(is(tok::html_end_tag)); 21206f32e7eSjoerg TextPtr = Name.data(); 21306f32e7eSjoerg IntVal = Name.size(); 21406f32e7eSjoerg } 21506f32e7eSjoerg 21606f32e7eSjoerg void dump(const Lexer &L, const SourceManager &SM) const; 21706f32e7eSjoerg }; 21806f32e7eSjoerg 21906f32e7eSjoerg /// Comment lexer. 22006f32e7eSjoerg class Lexer { 22106f32e7eSjoerg private: 22206f32e7eSjoerg Lexer(const Lexer &) = delete; 22306f32e7eSjoerg void operator=(const Lexer &) = delete; 22406f32e7eSjoerg 22506f32e7eSjoerg /// Allocator for strings that are semantic values of tokens and have to be 22606f32e7eSjoerg /// computed (for example, resolved decimal character references). 22706f32e7eSjoerg llvm::BumpPtrAllocator &Allocator; 22806f32e7eSjoerg 22906f32e7eSjoerg DiagnosticsEngine &Diags; 23006f32e7eSjoerg 23106f32e7eSjoerg const CommandTraits &Traits; 23206f32e7eSjoerg 23306f32e7eSjoerg const char *const BufferStart; 23406f32e7eSjoerg const char *const BufferEnd; 23506f32e7eSjoerg 23606f32e7eSjoerg const char *BufferPtr; 23706f32e7eSjoerg 23806f32e7eSjoerg /// One past end pointer for the current comment. For BCPL comments points 23906f32e7eSjoerg /// to newline or BufferEnd, for C comments points to star in '*/'. 24006f32e7eSjoerg const char *CommentEnd; 24106f32e7eSjoerg 242*13fbcb42Sjoerg SourceLocation FileLoc; 243*13fbcb42Sjoerg 244*13fbcb42Sjoerg /// If true, the commands, html tags, etc will be parsed and reported as 245*13fbcb42Sjoerg /// separate tokens inside the comment body. If false, the comment text will 246*13fbcb42Sjoerg /// be parsed into text and newline tokens. 247*13fbcb42Sjoerg bool ParseCommands; 248*13fbcb42Sjoerg 249*13fbcb42Sjoerg enum LexerCommentState : uint8_t { 25006f32e7eSjoerg LCS_BeforeComment, 25106f32e7eSjoerg LCS_InsideBCPLComment, 25206f32e7eSjoerg LCS_InsideCComment, 25306f32e7eSjoerg LCS_BetweenComments 25406f32e7eSjoerg }; 25506f32e7eSjoerg 25606f32e7eSjoerg /// Low-level lexer state, track if we are inside or outside of comment. 25706f32e7eSjoerg LexerCommentState CommentState; 25806f32e7eSjoerg 259*13fbcb42Sjoerg enum LexerState : uint8_t { 26006f32e7eSjoerg /// Lexing normal comment text 26106f32e7eSjoerg LS_Normal, 26206f32e7eSjoerg 26306f32e7eSjoerg /// Finished lexing verbatim block beginning command, will lex first body 26406f32e7eSjoerg /// line. 26506f32e7eSjoerg LS_VerbatimBlockFirstLine, 26606f32e7eSjoerg 26706f32e7eSjoerg /// Lexing verbatim block body line-by-line, skipping line-starting 26806f32e7eSjoerg /// decorations. 26906f32e7eSjoerg LS_VerbatimBlockBody, 27006f32e7eSjoerg 27106f32e7eSjoerg /// Finished lexing verbatim line beginning command, will lex text (one 27206f32e7eSjoerg /// line). 27306f32e7eSjoerg LS_VerbatimLineText, 27406f32e7eSjoerg 27506f32e7eSjoerg /// Finished lexing \verbatim <TAG \endverbatim part, lexing tag attributes. 27606f32e7eSjoerg LS_HTMLStartTag, 27706f32e7eSjoerg 27806f32e7eSjoerg /// Finished lexing \verbatim </TAG \endverbatim part, lexing '>'. 27906f32e7eSjoerg LS_HTMLEndTag 28006f32e7eSjoerg }; 28106f32e7eSjoerg 28206f32e7eSjoerg /// Current lexing mode. 28306f32e7eSjoerg LexerState State; 28406f32e7eSjoerg 28506f32e7eSjoerg /// If State is LS_VerbatimBlock, contains the name of verbatim end 28606f32e7eSjoerg /// command, including command marker. 28706f32e7eSjoerg SmallString<16> VerbatimBlockEndCommandName; 28806f32e7eSjoerg 28906f32e7eSjoerg /// Given a character reference name (e.g., "lt"), return the character that 29006f32e7eSjoerg /// it stands for (e.g., "<"). 29106f32e7eSjoerg StringRef resolveHTMLNamedCharacterReference(StringRef Name) const; 29206f32e7eSjoerg 29306f32e7eSjoerg /// Given a Unicode codepoint as base-10 integer, return the character. 29406f32e7eSjoerg StringRef resolveHTMLDecimalCharacterReference(StringRef Name) const; 29506f32e7eSjoerg 29606f32e7eSjoerg /// Given a Unicode codepoint as base-16 integer, return the character. 29706f32e7eSjoerg StringRef resolveHTMLHexCharacterReference(StringRef Name) const; 29806f32e7eSjoerg 29906f32e7eSjoerg void formTokenWithChars(Token &Result, const char *TokEnd, 30006f32e7eSjoerg tok::TokenKind Kind); 30106f32e7eSjoerg formTextToken(Token & Result,const char * TokEnd)30206f32e7eSjoerg void formTextToken(Token &Result, const char *TokEnd) { 30306f32e7eSjoerg StringRef Text(BufferPtr, TokEnd - BufferPtr); 30406f32e7eSjoerg formTokenWithChars(Result, TokEnd, tok::text); 30506f32e7eSjoerg Result.setText(Text); 30606f32e7eSjoerg } 30706f32e7eSjoerg getSourceLocation(const char * Loc)30806f32e7eSjoerg SourceLocation getSourceLocation(const char *Loc) const { 30906f32e7eSjoerg assert(Loc >= BufferStart && Loc <= BufferEnd && 31006f32e7eSjoerg "Location out of range for this buffer!"); 31106f32e7eSjoerg 31206f32e7eSjoerg const unsigned CharNo = Loc - BufferStart; 31306f32e7eSjoerg return FileLoc.getLocWithOffset(CharNo); 31406f32e7eSjoerg } 31506f32e7eSjoerg Diag(SourceLocation Loc,unsigned DiagID)31606f32e7eSjoerg DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) { 31706f32e7eSjoerg return Diags.Report(Loc, DiagID); 31806f32e7eSjoerg } 31906f32e7eSjoerg 32006f32e7eSjoerg /// Eat string matching regexp \code \s*\* \endcode. 32106f32e7eSjoerg void skipLineStartingDecorations(); 32206f32e7eSjoerg 32306f32e7eSjoerg /// Lex comment text, including commands if ParseCommands is set to true. 32406f32e7eSjoerg void lexCommentText(Token &T); 32506f32e7eSjoerg 32606f32e7eSjoerg void setupAndLexVerbatimBlock(Token &T, const char *TextBegin, char Marker, 32706f32e7eSjoerg const CommandInfo *Info); 32806f32e7eSjoerg 32906f32e7eSjoerg void lexVerbatimBlockFirstLine(Token &T); 33006f32e7eSjoerg 33106f32e7eSjoerg void lexVerbatimBlockBody(Token &T); 33206f32e7eSjoerg 33306f32e7eSjoerg void setupAndLexVerbatimLine(Token &T, const char *TextBegin, 33406f32e7eSjoerg const CommandInfo *Info); 33506f32e7eSjoerg 33606f32e7eSjoerg void lexVerbatimLineText(Token &T); 33706f32e7eSjoerg 33806f32e7eSjoerg void lexHTMLCharacterReference(Token &T); 33906f32e7eSjoerg 34006f32e7eSjoerg void setupAndLexHTMLStartTag(Token &T); 34106f32e7eSjoerg 34206f32e7eSjoerg void lexHTMLStartTag(Token &T); 34306f32e7eSjoerg 34406f32e7eSjoerg void setupAndLexHTMLEndTag(Token &T); 34506f32e7eSjoerg 34606f32e7eSjoerg void lexHTMLEndTag(Token &T); 34706f32e7eSjoerg 34806f32e7eSjoerg public: 34906f32e7eSjoerg Lexer(llvm::BumpPtrAllocator &Allocator, DiagnosticsEngine &Diags, 35006f32e7eSjoerg const CommandTraits &Traits, SourceLocation FileLoc, 35106f32e7eSjoerg const char *BufferStart, const char *BufferEnd, 35206f32e7eSjoerg bool ParseCommands = true); 35306f32e7eSjoerg 35406f32e7eSjoerg void lex(Token &T); 35506f32e7eSjoerg 35606f32e7eSjoerg StringRef getSpelling(const Token &Tok, const SourceManager &SourceMgr) const; 35706f32e7eSjoerg }; 35806f32e7eSjoerg 35906f32e7eSjoerg } // end namespace comments 36006f32e7eSjoerg } // end namespace clang 36106f32e7eSjoerg 36206f32e7eSjoerg #endif 36306f32e7eSjoerg 364