%{ # -*- mode: C++ -*- from gyb_syntax_support import * from gyb_syntax_support.Trivia import TRIVIAS # Ignore the following admonition; it applies to the resulting .h file only }% //// Automatically Generated From Trivia.h.gyb. //// Do Not Edit Directly! //===--- Trivia.h - Swift Syntax Trivia Interface ---------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file defines a data structure representing "trivia" in the Swift // language, such as formatting text like whitespace, or other pieces of // syntax that don't affect program behavior, like comments. // // All source trivia except for comments are kind of "run-length encoded". // For example, a token might follow 2 newlines and 2 spaces, like so: // // func foo() { // var x = 2 // } // // Here, the 'var' keyword would have the following "leading" trivia: // [ Newlines(2), Spaces(2) ] // // and the following "trailing" trivia: // [ Spaces(1) ] // // Every terminal token in the tree has "leading" and "trailing" trivia. // // There is one basic rule to follow when attaching trivia: // // 1. A token owns all of its trailing trivia up to, but not including, // the next newline character. // // 2. Looking backward in the text, a token owns all of the leading trivia // up to and including the first contiguous sequence of newlines characters. // // For this example again: // // func foo() { // var x = 2 // } // // 'func' // - Has no leading trivia. // - Takes up the space after because of rule 1. // - Leading: [] Trailing: [ Space(1) ] // // 'foo' // - Has no leading trivia. 'func' ate it as its trailing trivia. // - Has no trailing trivia, because it is butted up against the next '('. // - Leading: [] Trailing: [] // // '(' // - Has no leading or trailing trivia. // - Leading: [] Trailing: [] // // ')' // - Has no leading trivia. // - Takes up the space after because of rule 1. // - Leading: [] Trailing: [ Space(1) ] // // '{' // - Has no leading trivia. ')' ate it as its trailing trivia. // - Has no trailing trivia. Because of Rule 1, it doesn't take the newline. // - Leading: [] Trailing: [] // // 'var' // - Takes the newline and preceding two spaces because of Rule 2. // - Takes the single space that follows because of Rule 1. // - Leading: [ Newline(1), Space(2) ] Trailing: [ Space(1) ] // // ... and so on. // //===----------------------------------------------------------------------===// #ifndef SWIFT_SYNTAX_TRIVIA_H #define SWIFT_SYNTAX_TRIVIA_H #include "swift/Basic/OwnedString.h" #include "swift/Basic/JSONSerialization.h" #include "swift/Basic/ByteTreeSerialization.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/YAMLTraits.h" #include namespace swift { namespace syntax { class AbsolutePosition; /// The kind of source trivia, such as spaces, newlines, or comments. enum class TriviaKind { % for trivia in TRIVIAS: // ${trivia.comment} ${trivia.name}, % end }; bool isCommentTriviaKind(TriviaKind kind); /// A contiguous stretch of a single kind of trivia. The constituent part of /// a `Trivia` collection. /// /// For example, four spaces would be represented by /// { TriviaKind::Space, 4, "" }. /// /// All trivia except for comments don't need to store text, since they can be /// reconstituted using their Kind and Count. /// /// In general, you should deal with the actual Trivia collection instead /// of individual pieces whenever possible. class TriviaPiece { TriviaKind Kind; unsigned Count; OwnedString Text; TriviaPiece(const TriviaKind Kind, const OwnedString Text) : Kind(Kind), Count(1), Text(Text) {} TriviaPiece(const TriviaKind Kind, const unsigned Count) : Kind(Kind), Count(Count), Text() {} friend struct json::ObjectTraits; friend struct llvm::yaml::MappingTraits; public: % for trivia in TRIVIAS: % if trivia.is_collection(): static TriviaPiece ${trivia.lower_name}s(unsigned Count) { return {TriviaKind::${trivia.name}, Count}; } static TriviaPiece ${trivia.lower_name}() { return ${trivia.lower_name}s(1); } % else: static TriviaPiece ${trivia.lower_name}(const OwnedString Text) { return {TriviaKind::${trivia.name}, Text}; } % end % end static TriviaPiece fromText(TriviaKind kind, StringRef text); /// Return kind of the trivia. TriviaKind getKind() const { return Kind; } /// Return the text of the trivia. StringRef getText() const { return Text.str(); } /// Return the text of the trivia. unsigned getCount() const { return Count; } /// Return textual length of the trivia. size_t getTextLength() const { switch (Kind) { % for trivia in TRIVIAS: case TriviaKind::${trivia.name}: % if trivia.is_collection(): return Count * ${trivia.characters_len()}; % else: return Text.size(); % end % end } llvm_unreachable("unhandled kind"); } bool isComment() const { return isCommentTriviaKind(getKind()); } void accumulateAbsolutePosition(AbsolutePosition &Pos) const; /// Try to compose this and Next to one TriviaPiece. /// It returns true if it is succeeded. bool trySquash(const TriviaPiece &Next); /// Print a debug representation of this trivia piece to the provided output /// stream and indentation level. void dump(llvm::raw_ostream &OS, unsigned Indent = 0) const; /// Print this piece of trivia to the provided output stream. void print(llvm::raw_ostream &OS) const; bool operator==(const TriviaPiece &Other) const { return Kind == Other.Kind && Count == Other.Count && Text.str().compare(Other.Text.str()) == 0; } bool operator!=(const TriviaPiece &Other) const { return !(*this == Other); } void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(unsigned(Kind)); switch (Kind) { % for trivia in TRIVIAS: case TriviaKind::${trivia.name}: % if trivia.is_collection(): ID.AddInteger(Count); % else: ID.AddString(Text.str()); % end break; % end } } }; using TriviaList = std::vector; /// A collection of leading or trailing trivia. This is the main data structure /// for thinking about trivia. struct Trivia { TriviaList Pieces; /// Get the begin iterator of the pieces. TriviaList::const_iterator begin() const { return Pieces.begin(); } /// Get the end iterator of the pieces. TriviaList::const_iterator end() const { return Pieces.end(); } /// Add a piece to the end of the collection. void push_back(const TriviaPiece &Piece) { Pieces.push_back(Piece); } /// Add a piece to the beginning of the collection. void push_front(const TriviaPiece &Piece) { Pieces.insert(Pieces.begin(), Piece); } /// Clear pieces. void clear() { Pieces.clear(); } /// Return a reference to the first piece. /// /// Precondition: !empty() const TriviaPiece &front() const { assert(!empty()); return Pieces.front(); } /// Return a reference to the last piece. /// /// Precondition: !empty() const TriviaPiece &back() const { assert(!empty()); return Pieces.back(); } /// Remove the last piece from the Trivia collection. /// /// Precondition: !empty() void pop_back() { assert(!empty()); Pieces.pop_back(); } /// Returns true if there are no pieces in this Trivia collection. bool empty() const { return Pieces.empty(); } /// Return the number of pieces in this Trivia collection. size_t size() const { return Pieces.size(); } size_t getTextLength() const { size_t Len = 0; for (auto &P : Pieces) Len += P.getTextLength(); return Len; } /// Append Next TriviaPiece or compose last TriviaPiece and /// Next TriviaPiece to one last TriviaPiece if it can. void appendOrSquash(const TriviaPiece &Next); /// Dump a debug representation of this Trivia collection to standard error. void dump() const; /// Dump a debug representation of this Trivia collection to the provided /// stream and indentation level. void dump(llvm::raw_ostream &OS, unsigned Indent = 0) const; /// Print all of the pieces to the provided output stream in source order. void print(llvm::raw_ostream &OS) const; /// Return a new Trivia collection by appending pieces from `Other`. Trivia appending(const Trivia &Other) const; Trivia operator+(const Trivia &Other) const; /// Look for the first TriviaPiece with the DesiredKind. If not found, /// returns the end iterator. TriviaList::const_iterator find(const TriviaKind DesiredKind) const; /// Returns true if the Trivia collection contains a piece of the given Kind. bool contains(const TriviaKind Kind) const { return find(Kind) != end(); } bool operator==(const Trivia &Other) const { if (Pieces.size() != Other.size()) { return false; } for (size_t i = 0; i < Pieces.size(); ++i) { if (Pieces[i] != Other.Pieces[i]) { return false; } } return true; } bool operator!=(const Trivia &Other) const { return !(*this == Other); } % for trivia in TRIVIAS: % if trivia.is_collection(): static Trivia ${trivia.lower_name}s(unsigned Count) { if (Count == 0) { return {}; } return {{TriviaPiece::${trivia.lower_name}s(Count)}}; } static Trivia ${trivia.lower_name}() { return {{TriviaPiece::${trivia.lower_name}s(1)}}; } % else: static Trivia ${trivia.lower_name}(const OwnedString Text) { assert(checkTriviaText(Text.str(), TriviaKind::${trivia.name})); return {{TriviaPiece::${trivia.lower_name}(Text)}}; } % end % end private: static bool checkTriviaText(StringRef Text, TriviaKind Kind) { switch(Kind) { case TriviaKind::LineComment: return Text.startswith("//"); case TriviaKind::BlockComment: return Text.startswith("/*") && Text.endswith("*/"); case TriviaKind::DocLineComment: return Text.startswith("///"); case TriviaKind::DocBlockComment: return Text.startswith("/**") && Text.endswith("*/"); case TriviaKind::GarbageText: return !Text.empty(); % for trivia in TRIVIAS: % if trivia.is_collection(): case TriviaKind::${trivia.name}: return true; % end % end } } }; } // namespace syntax namespace byteTree { template <> struct WrapperTypeTraits { static uint8_t numericValue(const syntax::TriviaKind &Kind) { switch (Kind) { % for trivia in TRIVIAS: case syntax::TriviaKind::${trivia.name}: return ${trivia.serialization_code}; % end } llvm_unreachable("unhandled kind"); } static void write(ByteTreeWriter &Writer, const syntax::TriviaKind &Kind, unsigned Index) { Writer.write(numericValue(Kind), Index); } }; template <> struct ObjectTraits { static unsigned numFields(const syntax::TriviaPiece &Trivia, UserInfoMap &UserInfo) { return 2; } static void write(ByteTreeWriter &Writer, const syntax::TriviaPiece &Trivia, UserInfoMap &UserInfo) { Writer.write(Trivia.getKind(), /*Index=*/0); // Write the trivia's text or count depending on its kind switch (Trivia.getKind()) { % for trivia in TRIVIAS: case syntax::TriviaKind::${trivia.name}: % if trivia.is_collection(): Writer.write(static_cast(Trivia.getCount()), /*Index=*/1); % else: Writer.write(Trivia.getText(), /*Index=*/1); % end break; % end } } }; } // end namespace byteTree namespace json { /// Serialization traits for TriviaPiece. /// - All trivia pieces will have a "kind" key that contains the serialized /// name of the trivia kind. /// - Comment trivia will have the associated text of the comment under the /// "value" key. /// - All other trivia will have the associated integer count of their /// occurrences under the "value" key. template<> struct ObjectTraits { static void mapping(Output &out, syntax::TriviaPiece &value) { auto kind = value.getKind(); out.mapRequired("kind", kind); switch (kind) { % for trivia in TRIVIAS: case syntax::TriviaKind::${trivia.name}: { % if trivia.is_collection(): auto count = value.getCount(); out.mapRequired("value", count); % else: auto text = value.getText(); out.mapRequired("value", text); % end break; } % end } } }; /// Serialization traits for TriviaKind. template <> struct ScalarReferenceTraits { static StringRef stringRef(const syntax::TriviaKind &value) { switch (value) { % for trivia in TRIVIAS: case syntax::TriviaKind::${trivia.name}: return "\"${trivia.name}\""; % end } llvm_unreachable("unhandled kind"); } static bool mustQuote(StringRef) { // The string is already quoted. This is more efficient since it does not // check for characters that need to be escaped return false; } }; } // namespace json } // namespace swift namespace llvm { namespace yaml { /// Deserialization traits for TriviaPiece. /// - All trivia pieces will have a "kind" key that contains the serialized /// name of the trivia kind. /// - Comment trivia will have the associated text of the comment under the /// "value" key. /// - All other trivia will have the associated integer count of their /// occurrences under the "value" key. template<> struct MappingTraits { static swift::syntax::TriviaPiece mapping(IO &in) { swift::syntax::TriviaKind kind; in.mapRequired("kind", kind); switch (kind) { % for trivia in TRIVIAS: case swift::syntax::TriviaKind::${trivia.name}: { % if trivia.is_collection(): /// FIXME: This is a workaround for existing bug from llvm yaml parser /// which would raise error when deserializing number with trailing character /// like "1\n". See https://bugs.llvm.org/show_bug.cgi?id=15505 StringRef str; in.mapRequired("value", str); unsigned count = std::atoi(str.data()); return swift::syntax::TriviaPiece(kind, count); % else: StringRef text; in.mapRequired("value", text); return swift::syntax::TriviaPiece( kind, swift::OwnedString::makeRefCounted(text)); % end break; } % end } } }; /// Deserialization traits for TriviaKind. template <> struct ScalarEnumerationTraits { static void enumeration(IO &in, swift::syntax::TriviaKind &value) { % for trivia in TRIVIAS: in.enumCase(value, "${trivia.name}", swift::syntax::TriviaKind::${trivia.name}); % end } }; } // namespace yaml } // namespace llvm #endif // SWIFT_SYNTAX_TRIVIA_H