| %{ |
| # -*- 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/Debug.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 <vector> |
| |
| 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<TriviaPiece>; |
| friend struct llvm::yaml::MappingTraits<TriviaPiece>; |
| |
| 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<TriviaPiece>; |
| |
| /// 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. |
| SWIFT_DEBUG_DUMP; |
| |
| /// 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<syntax::TriviaKind> { |
| 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<syntax::TriviaPiece> { |
| 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<uint32_t>(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<syntax::TriviaPiece> { |
| 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<syntax::TriviaKind> { |
| 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<swift::syntax::TriviaPiece> { |
| 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 |
| } |
| llvm_unreachable("covered switch"); |
| } |
| }; |
| |
| /// Deserialization traits for TriviaKind. |
| template <> |
| struct ScalarEnumerationTraits<swift::syntax::TriviaKind> { |
| 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 |