Files
swift-mirror/include/swift/Syntax/Serialization/SyntaxDeserialization.h
Alex Hoppen ac512d4341 [libSyntax] Add a reference counted version of OwnedString
We cannot use unowned strings for token texts of incrementally parsed
syntax trees since the source buffer to which reused nodes refer will
have been freed for reused nodes. Always copying the token text whenever
OwnedString is passed is too expensive. A reference counted copy of the
string allows us to keep the token's string alive across incremental
parses while eliminating unnecessary copies.
2018-08-13 15:37:53 -07:00

208 lines
7.1 KiB
C++

//===--- SyntaxDeserialization.h - Swift Syntax Deserialization --*- 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 provides the deserialization from JSON to RawSyntax nodes and their
// constituent parts.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SYNTAX_SERIALIZATION_SYNTAXDESERIALIZATION_H
#define SWIFT_SYNTAX_SERIALIZATION_SYNTAXDESERIALIZATION_H
#include "swift/Basic/StringExtras.h"
#include "swift/Syntax/RawSyntax.h"
#include "swift/Syntax/SyntaxNodes.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/YAMLTraits.h"
#include <forward_list>
namespace llvm {
namespace yaml {
/// Deserialization traits for SourcePresence.
template <> struct ScalarEnumerationTraits<swift::SourcePresence> {
static void enumeration(IO &in, swift::SourcePresence &value) {
in.enumCase(value, "Present", swift::SourcePresence::Present);
in.enumCase(value, "Missing", swift::SourcePresence::Missing);
}
};
/// Deserialization traits for swift::tok.
template <> struct ScalarEnumerationTraits<swift::tok> {
static void enumeration(IO &in, swift::tok &value) {
#define TOKEN(name) in.enumCase(value, #name, swift::tok::name);
#include "swift/Syntax/TokenKinds.def"
}
};
/// Deserialization traits for Trivia.
/// An array of the underlying TriviaPieces will be deserialized as Trivia.
template <typename Context> swift::TriviaPiece yamlize(IO &io, Context &Ctx) {
io.beginMapping();
auto ret = MappingTraits<swift::TriviaPiece>::mapping(io);
io.endMapping();
return ret;
}
template <typename Context>
void yamlize(IO &io, std::vector<swift::TriviaPiece> &Seq, bool, Context &Ctx) {
unsigned incnt = io.beginSequence();
for (unsigned i = 0; i < incnt; ++i) {
void *SaveInfo;
if (io.preflightElement(i, SaveInfo)) {
Seq.push_back(yamlize(io, Ctx));
io.postflightElement(SaveInfo);
}
}
io.endSequence();
}
template <> struct SequenceTraits<std::vector<swift::TriviaPiece>> {
static size_t size(IO &in, std::vector<swift::TriviaPiece> &seq) {
return seq.size();
}
};
/// Deserialization traits for RawSyntax list.
template <> struct SequenceTraits<std::vector<swift::RC<swift::RawSyntax>>> {
static size_t size(IO &in, std::vector<swift::RC<swift::RawSyntax>> &seq) {
return seq.size();
}
static swift::RC<swift::RawSyntax> &
element(IO &in, std::vector<swift::RC<swift::RawSyntax>> &seq, size_t index) {
if (seq.size() <= index) {
seq.resize(index + 1);
}
return const_cast<swift::RC<swift::RawSyntax> &>(seq[index]);
}
};
/// An adapter struct that provides a nested structure for token content.
struct TokenDescription {
bool hasValue = false;
swift::tok Kind;
StringRef Text;
};
/// Desrialization traits for TokenDescription.
/// TokenDescriptions always serialized with a token kind, which is
/// the stringified version of their name in the tok:: enum.
/// ```
/// {
/// "kind": <token name, e.g. "kw_struct">,
/// }
/// ```
///
/// For tokens that have some kind of text attached, like literals or
/// identifiers, the serialized form will also have a "text" key containing
/// that text as the value.
template <> struct MappingTraits<TokenDescription> {
static void mapping(IO &in, TokenDescription &value) {
in.mapRequired("kind", value.Kind);
value.hasValue = true;
if (!swift::isTokenTextDetermined(value.Kind)) {
in.mapRequired("text", value.Text);
} else {
value.Text = swift::getTokenText(value.Kind);
}
}
};
/// Deserialization traits for RC<RawSyntax>.
/// First it will check whether the node is null.
/// Then this will be different depending if the raw syntax node is a Token or
/// not. Token nodes will always have this structure:
/// ```
/// {
/// "tokenKind": { "kind": <token kind>, "text": <token text> },
/// "leadingTrivia": [ <trivia pieces...> ],
/// "trailingTrivia": [ <trivia pieces...> ],
/// "presence": <"Present" or "Missing">
/// }
/// ```
/// All other raw syntax nodes will have this structure:
/// ```
/// {
/// "kind": <syntax kind>,
/// "layout": [ <raw syntax nodes...> ],
/// "presence": <"Present" or "Missing">
/// }
/// ```
template <> struct MappingTraits<swift::RC<swift::RawSyntax>> {
static void mapping(IO &in, swift::RC<swift::RawSyntax> &value) {
TokenDescription description;
auto input = static_cast<Input *>(&in);
/// Check whether this is null
if (input->getCurrentNode()->getType() != Node::NodeKind::NK_Mapping) {
return;
}
in.mapOptional("tokenKind", description);
if (description.hasValue) {
swift::tok tokenKind = description.Kind;
StringRef text = description.Text;
std::vector<swift::TriviaPiece> leadingTrivia;
in.mapRequired("leadingTrivia", leadingTrivia);
std::vector<swift::TriviaPiece> trailingTrivia;
in.mapRequired("trailingTrivia", trailingTrivia);
swift::SourcePresence presence;
in.mapRequired("presence", presence);
/// 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 nodeIdString;
in.mapRequired("id", nodeIdString);
unsigned nodeId = std::atoi(nodeIdString.data());
value = swift::RawSyntax::make(
tokenKind, swift::OwnedString::makeRefCounted(text), leadingTrivia,
trailingTrivia, presence, /*Arena=*/nullptr, nodeId);
} else {
swift::SyntaxKind kind;
in.mapRequired("kind", kind);
std::vector<swift::RC<swift::RawSyntax>> layout;
in.mapRequired("layout", layout);
swift::SourcePresence presence;
in.mapRequired("presence", presence);
/// 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 nodeIdString;
in.mapRequired("id", nodeIdString);
unsigned nodeId = std::atoi(nodeIdString.data());
value = swift::RawSyntax::make(kind, layout, presence, /*Arena=*/nullptr,
nodeId);
}
}
};
} // end namespace yaml
} // end namespace llvm
namespace swift {
namespace json {
class SyntaxDeserializer {
llvm::yaml::Input Input;
public:
SyntaxDeserializer(llvm::StringRef InputContent) : Input(InputContent) {}
SyntaxDeserializer(llvm::MemoryBufferRef buffer) : Input(buffer) {}
llvm::Optional<swift::SourceFileSyntax> getSourceFileSyntax() {
swift::RC<swift::RawSyntax> raw;
Input >> raw;
return swift::make<swift::SourceFileSyntax>(raw);
}
};
} // namespace json
} // namespace swift
#endif /* SWIFT_SYNTAX_SERIALIZATION_SYNTAXDESERIALIZATION_H */