blob: 0f8994cc9c715dd55d201866a009bc7aa72f8bcd [file] [log] [blame]
%{
# -*- 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 <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.
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<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
}
}
};
/// 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