blob: e96b196da88449e923da15206caf929c29b448e3 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TOOLS_FIDL_FIDLC_INCLUDE_FIDL_NEW_SYNTAX_CONVERSION_H_
#define TOOLS_FIDL_FIDLC_INCLUDE_FIDL_NEW_SYNTAX_CONVERSION_H_
// A Conversion is an object that applies a specific translation from one syntax
// to another. Conversions may nest other conversions, such that they may use
// the output of the conversion of their nested children when creating their own
// output.
#include <optional>
#include <string>
#include "raw_ast.h"
#include "underlying_type.h"
#include "utils.h"
namespace fidl::conv {
// CopyRange is very similar to SourceElement, except that it does not need to
// map from the source file text to a syntax tree node exactly. Instead, it
// merely specifies the span between two "convertible" portions of the source
// file.
class CopyRange {
public:
CopyRange(const char* from, const char* until) : from_(from), until_(until) {}
const char* from_;
const char* until_;
};
class Conversion {
public:
Conversion() = default;
virtual ~Conversion() = default;
// Some conversions start with a span of text that can be copied character for
// character. For example, consider the following const declaration, written
// in the old syntax.
//
// const uint8 FOO = 5;
// <--A--|----B----|-C-->
//
// Span B is the portion of text being converted (done in this case via a
// NameAndTypeConversion). Spans A and C do not need to be converted, and can
// be copied verbatim. The CopyRange describing SpanA would thus be passed to
// the AddPrefix() method of the NameAndTypeConversion, while C would be
// included in the prefix of whatever conversion comes next.
void AddPrefix(std::unique_ptr<CopyRange> copy_range) {
copy_ranges_.emplace_back(std::move(copy_range));
}
// A conversion that nests other conversions inside of itself enables this
// method in order to ingest the results of those child conversions. For
// example, consider the following alias declaration, written in the old
// syntax:
//
// alias my_type = array<handle:<PORT,7>?>:5;
// |--------A-------|
// |------------B------------|
//
// Conversion A (for the "handle" type declaration) and is nested within
// conversion B (for "array"). When the inner conversion is resolved and
// stringified via its Write() method (to "handle:<optional,PORT,7>" in this
// case), its result must be passed up the Conversion object handling the
// outer conversion, which will use it like "array<[CONV_A_OUTPUT],5>"
virtual void AddChildText(std::string child) = 0;
// Write produces a string of converted text, and contains the logic for
// taking the SourceElement of the node being converted, along with any child
// text that has been attached, and creating the converted output.
virtual std::string Write(fidl::utils::Syntax syntax) = 0;
protected:
std::string prefix() {
std::string out;
for (auto& copy_range : copy_ranges_) {
out += std::string(copy_range->from_, copy_range->until_);
}
return out;
}
private:
std::vector<std::unique_ptr<CopyRange>> copy_ranges_;
};
// A conversion that leaves its contents exactly as it found them. This is
// useful for cases where a span is only converted in certain cases.
class NoopConversion : public Conversion {
public:
explicit NoopConversion(const Token& start, const Token& end) : start_(start), end_(end) {}
~NoopConversion() override = default;
const Token& start_;
const Token& end_;
void AddChildText(std::string child) override {}
std::string Write(fidl::utils::Syntax syntax) override {
const char* from = start_.data().data();
const char* until = end_.data().data() + end_.data().length();
return prefix() + std::string(from, until);
}
};
// Converts a single attribute, one of potentially several in an AttributeList.
class AttributeConversion : public Conversion {
public:
AttributeConversion(const std::string& name, const std::string& value)
: name_(name), value_(value) {}
~AttributeConversion() override = default;
const std::string& name_;
const std::string& value_;
void AddChildText(std::string child) override {}
std::string Write(fidl::utils::Syntax syntax) override;
};
// Handles an AttributeList. Such lists have one peculiarity to be aware of,
// which is the special handling they require when they include doc comments.
// Unlike regular attributes, doc comments need not be converted, and should not
// appear in the bracketed attribute list. Because such comments are always
// first in the AttributeList if they exist, we can just check if the first
// comment is a doc comment, and special case its conversion.
class AttributeListConversion : public Conversion {
public:
explicit AttributeListConversion(bool has_doc_comment) : has_doc_comment_(has_doc_comment) {}
~AttributeListConversion() override = default;
std::vector<std::string> attributes_;
bool has_doc_comment_;
void AddChildText(std::string child) override { attributes_.push_back(child); }
std::string Write(fidl::utils::Syntax syntax) override;
};
// TypeConversion encapsulates the complex logic for converting various type
// definitions from the old syntax to the new. It may nest other
// TypeConversions, as would be the case for something like "vector<handle?>."
class TypeConversion : public Conversion {
public:
explicit TypeConversion(const std::unique_ptr<raw::TypeConstructorOld>& type_ctor,
const UnderlyingType underlying_type)
: type_ctor_(type_ctor), underlying_type_(underlying_type) {}
~TypeConversion() override = default;
const std::unique_ptr<raw::TypeConstructorOld>& type_ctor_;
const UnderlyingType underlying_type_;
std::string wrapped_type_text_;
void AddChildText(std::string child) override { wrapped_type_text_ = child; }
std::string Write(fidl::utils::Syntax syntax) override;
};
// Ensures that we always use the "alias" keyword, instead of the now-deprecated
// "using."
class UsingKeywordConversion : public Conversion {
public:
UsingKeywordConversion(const std::unique_ptr<raw::CompoundIdentifier>& name,
const std::unique_ptr<raw::TypeConstructorOld>& type_ctor)
: name_(name), type_ctor_(type_ctor) {}
~UsingKeywordConversion() override = default;
const std::unique_ptr<raw::CompoundIdentifier>& name_;
const std::unique_ptr<raw::TypeConstructorOld>& type_ctor_;
std::string type_text_;
void AddChildText(std::string child) override { type_text_ = child; }
std::string Write(fidl::utils::Syntax syntax) override;
};
// Handles the application of the "types come second" rule specified by FTP-050.
// For example, this is the conversion used to turn "uint8 FOO" into "FOO
// uint8." The NameAndTypeConversion always nests a TypeConversion.
class NameAndTypeConversion : public Conversion {
public:
NameAndTypeConversion(const std::unique_ptr<raw::Identifier>& identifier,
const std::unique_ptr<raw::TypeConstructorOld>& type_ctor)
: identifier_(identifier), type_ctor_(type_ctor) {}
~NameAndTypeConversion() override = default;
const std::unique_ptr<raw::Identifier>& identifier_;
const std::unique_ptr<raw::TypeConstructorOld>& type_ctor_;
std::string type_text_;
void AddChildText(std::string child) override { type_text_ = child; }
std::string Write(fidl::utils::Syntax syntax) override;
};
// An abstract class for the conversion of "membered" types, ie types that may
// have an arbitrary number of members defined in a "{...}" block. Examples of
// such types include protocol, struct, table, union, etc.
//
// All such types have three common properties: they may or may not specify
// "resourceness," they may or may not specify "strictness," and they must have
// one ore more member types declared in their "{...}" block.
class MemberedDeclarationConversion : public Conversion {
public:
MemberedDeclarationConversion(const std::unique_ptr<raw::Identifier>& identifier,
const types::Resourceness& resourceness)
: identifier_(identifier), resourceness_(resourceness) {}
~MemberedDeclarationConversion() override = default;
const std::unique_ptr<raw::Identifier>& identifier_;
const types::Resourceness resourceness_;
std::vector<std::string> members_;
void AddChildText(std::string child) override { members_.push_back(child); }
std::string Write(fidl::utils::Syntax syntax) override;
protected:
virtual std::string get_fidl_type() = 0;
virtual std::string get_modifiers(fidl::utils::Syntax syntax) {
return resourceness_ == types::Resourceness::kResource ? "resource " : "";
}
std::string get_decl_str(fidl::utils::Syntax syntax) {
return get_modifiers(syntax) + get_fidl_type();
}
};
class FlexibleTypeConversion : public MemberedDeclarationConversion {
public:
FlexibleTypeConversion(const std::unique_ptr<raw::Identifier>& identifier,
const types::Resourceness& resourceness,
const std::optional<types::Strictness>& strictness)
: MemberedDeclarationConversion(identifier, resourceness), strictness_(strictness) {}
// this represents the modifier specified in source rather than the actual
// underlying strictness of the type, which is why the optional is required
// to represent the state of "no strictness specified".
std::optional<types::Strictness> strictness_;
protected:
std::string get_modifiers(fidl::utils::Syntax syntax) override {
std::string modifiers = MemberedDeclarationConversion::get_modifiers(syntax);
if (syntax == fidl::utils::Syntax::kNew) {
modifiers += ((strictness_.value_or(types::Strictness::kStrict) == types::Strictness::kStrict)
? "strict "
: "flexible ");
} else if (strictness_ != std::nullopt) {
if (strictness_.value() == types::Strictness::kStrict) {
modifiers += "strict ";
} else if (strictness_.value() == types::Strictness::kFlexible) {
modifiers += "flexible ";
}
}
return modifiers;
}
};
// Handles the conversion of a struct declaration, written in the old syntax as:
//
// [resource] struct S {...}
//
// The individual struct member conversions are meant to be nested within this
// one as NameAndTypeConversions using the AddChildText() method.
class StructDeclarationConversion : public MemberedDeclarationConversion {
public:
StructDeclarationConversion(const std::unique_ptr<raw::Identifier>& identifier,
const types::Resourceness& resourceness)
: MemberedDeclarationConversion(identifier, resourceness) {}
private:
std::string get_fidl_type() override { return "struct"; }
};
// Handles the conversion of a table declaration, written in the old syntax as:
//
// [resource] table T {...}
//
// The individual table member conversions are meant to be nested within this
// one as NameAndTypeConversions using the AddChildText() method.
class TableDeclarationConversion : public MemberedDeclarationConversion {
public:
TableDeclarationConversion(const std::unique_ptr<raw::Identifier>& identifier,
const types::Resourceness& resourceness)
: MemberedDeclarationConversion(identifier, resourceness) {}
private:
std::string get_fidl_type() override { return "table"; }
};
// Handles the conversion of a union declaration, written in the old syntax as:
//
// [resource ][flexible|strict] union U {...}
//
// The individual union member conversions are meant to be nested within this
// one as NameAndTypeConversions using the AddChildText() method.
class UnionDeclarationConversion : public FlexibleTypeConversion {
public:
UnionDeclarationConversion(const std::unique_ptr<raw::Identifier>& identifier,
const std::optional<types::Strictness>& strictness,
const types::Resourceness& resourceness)
: FlexibleTypeConversion(identifier, resourceness, strictness) {}
private:
std::string get_fidl_type() override { return "union"; }
};
// Handles the conversion of declarations specified using the bits keyword.
// It is similar to the MemberedDeclarationConversion that it wraps, but has to
// account for the possibility that a declaration contains an (optional)
// wrapped type, like "bits NAME : WRAPPED_TYPE {..."
class BitsDeclarationConversion : public FlexibleTypeConversion {
public:
BitsDeclarationConversion(
const std::unique_ptr<raw::Identifier>& identifier,
const std::optional<std::reference_wrapper<std::unique_ptr<raw::TypeConstructorOld>>>&
maybe_wrapped_type,
const std::optional<types::Strictness>& strictness)
: FlexibleTypeConversion(identifier, types::Resourceness::kValue, strictness),
maybe_wrapped_type_(maybe_wrapped_type) {}
const std::optional<std::reference_wrapper<std::unique_ptr<raw::TypeConstructorOld>>>
maybe_wrapped_type_;
void AddChildText(std::string child) override { members_.push_back(child); }
std::string Write(fidl::utils::Syntax syntax) override;
private:
std::string get_fidl_type() override { return "bits"; }
std::string get_wrapped_type() {
if (maybe_wrapped_type_.has_value()) {
return " : " + maybe_wrapped_type_.value().get()->copy_to_str();
}
return "";
}
};
// Identical to the BitsDeclarationConversion, except that it replaces the word
// "bits" with "enum."
class EnumDeclarationConversion : public BitsDeclarationConversion {
public:
EnumDeclarationConversion(
const std::unique_ptr<raw::Identifier>& identifier,
const std::optional<std::reference_wrapper<std::unique_ptr<raw::TypeConstructorOld>>>&
maybe_wrapped_type,
const std::optional<types::Strictness>& strictness)
: BitsDeclarationConversion(identifier, maybe_wrapped_type, strictness) {}
private:
std::string get_fidl_type() override { return "enum"; }
};
} // namespace fidl::conv
#endif // TOOLS_FIDL_FIDLC_INCLUDE_FIDL_NEW_SYNTAX_CONVERSION_H_