272 lines
8.8 KiB
C++
272 lines
8.8 KiB
C++
// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <utility>
|
|
|
|
#include <hilti/ast/expression.h>
|
|
#include <hilti/ast/forward.h>
|
|
#include <hilti/ast/node.h>
|
|
#include <hilti/base/util.h>
|
|
|
|
namespace hilti {
|
|
|
|
namespace attribute {
|
|
|
|
/**
|
|
* Represents a specific attribute. While we generally identify attributes by
|
|
* their names (`&...`), we wrap those strings into `Kind` instances so that we
|
|
* can (1) define global constants for all valid attribute names, and (2)
|
|
* retain a list of all known attributes for error checking.
|
|
*
|
|
* Note that generally, one should only use the global, pre-defined constants
|
|
* when referring to a specific attribute. Avoid creating new instances on the
|
|
* fly.
|
|
*/
|
|
class Kind {
|
|
public:
|
|
explicit Kind(std::string name) : _name(std::move(name)) { _register(); }
|
|
|
|
Kind() = delete;
|
|
Kind(const Kind& other) = default;
|
|
Kind(Kind&& other) = default;
|
|
|
|
~Kind() = default; // not unregistering here, so that attribute remains known
|
|
|
|
operator std::string() const { return _name; }
|
|
|
|
Kind& operator=(const Kind& other) = default;
|
|
Kind& operator=(Kind&& other) = default;
|
|
|
|
bool operator==(const Kind& other) const { return _name == other._name; }
|
|
bool operator!=(const Kind& other) const { return ! operator==(other); }
|
|
|
|
// Returns a corresponding attribute kind iff the string matches to one of
|
|
// the known attributes created so far. Otherwise throws an `out_of_range`
|
|
// exception.
|
|
static Kind fromString(const std::string_view& s) {
|
|
if ( _known_attributes && _known_attributes->count(std::string(s)) > 0 )
|
|
return Kind(std::string(s));
|
|
else
|
|
throw std::out_of_range("unknown attribute kind: " + std::string(s));
|
|
}
|
|
|
|
private:
|
|
void _register();
|
|
|
|
std::string _name = "<unset attribute>";
|
|
static std::set<std::string>* _known_attributes;
|
|
};
|
|
|
|
/** Returns whether `kind` is in `kinds` */
|
|
inline bool isOneOf(const Kind& kind, std::initializer_list<Kind> kinds) {
|
|
return std::find(kinds.begin(), kinds.end(), kind) != kinds.end();
|
|
}
|
|
|
|
inline auto to_string(const Kind& kind) { return std::string(kind); }
|
|
|
|
inline std::ostream& operator<<(std::ostream& out, const Kind& x) {
|
|
out << to_string(x);
|
|
return out;
|
|
}
|
|
|
|
namespace kind {
|
|
inline auto from_string(const std::string_view& s) { return Kind::fromString(s); }
|
|
|
|
// In the following, we predefine all attributes that are part of the HILTI language.
|
|
|
|
const Kind AlwaysEmit("&always-emit");
|
|
const Kind Anchor("&anchor");
|
|
const Kind Anonymous("&anonymous");
|
|
const Kind Convert("&convert");
|
|
const Kind Cxxname("&cxxname");
|
|
const Kind Debug("&debug");
|
|
const Kind Default("&default");
|
|
const Kind HavePrototype("&have_prototype");
|
|
const Kind Internal("&internal");
|
|
const Kind NeededByFeature("&needed-by-feature");
|
|
const Kind NoEmit("&no-emit");
|
|
const Kind Nosub("&nosub");
|
|
const Kind OnHeap("&on-heap");
|
|
const Kind Optional("&optional");
|
|
const Kind Priority("&priority");
|
|
const Kind RequiresTypeFeature("&requires-type-feature");
|
|
const Kind Static("&static");
|
|
|
|
} // namespace kind
|
|
} // namespace attribute
|
|
|
|
/** AST node for an attribute. */
|
|
class Attribute : public Node {
|
|
public:
|
|
/** Returns the kind of the attribute, derived from its tag. */
|
|
const auto& kind() const { return _kind; }
|
|
|
|
/** Returns true if an argument is associated with the attribute. */
|
|
auto hasValue() const { return child(0) != nullptr; }
|
|
|
|
/**
|
|
* Returns the attribute associated with the node.
|
|
*
|
|
* @exception `std::out_of_range` if the attribute does not have an argument
|
|
*/
|
|
Node* value() const { return child(0); }
|
|
|
|
/**
|
|
* Returns the expression argument associated with the attribute.
|
|
*
|
|
* @return the argument, or an error if the attribute does not have an
|
|
* argument, or if it's not an expression.
|
|
*/
|
|
Result<Expression*> valueAsExpression() const;
|
|
|
|
/**
|
|
* Returns the expression argument associated with the attribute as a
|
|
* string, assuming it represents a constant integer value.
|
|
*
|
|
* @return the argument, or an error if the attribute does not have an
|
|
* argument, or if it's not a constant string.
|
|
*/
|
|
Result<std::string> valueAsString() const;
|
|
|
|
/**
|
|
* Returns the expression argument associated with the attribute as a
|
|
* signed integer, assuming it represents a constant integer value. Both
|
|
* signed and unsigned integer values are accepted, with the latter cased
|
|
* into signed for the return value
|
|
*
|
|
* @return the argument, or an error if the attribute does not have an
|
|
* argument, or if it's not a constant integer.
|
|
*/
|
|
Result<int64_t> valueAsInteger() const;
|
|
|
|
/**
|
|
* Coerce the attribute's expression value to a specified type, modifying
|
|
* the node in place.
|
|
*
|
|
* @return A successful return value if either the coercion succeeded
|
|
* (then the result's value is true), or nothing was to be done (then the
|
|
* result's value is false); a failure if a coercion would have been
|
|
* necessary, but failed, or the attribute does not have a expression value.
|
|
*/
|
|
Result<bool> coerceValueTo(Builder* builder, QualifiedType* dst);
|
|
|
|
node::Properties properties() const final {
|
|
auto p = node::Properties{{"tag", to_string(kind())}};
|
|
return Node::properties() + std::move(p);
|
|
}
|
|
|
|
/**
|
|
* Factory for an attribute coming with an argument. The argument must be
|
|
* an AST node representing an expression.
|
|
*
|
|
* @param kind the attribute's internal representation
|
|
* @param v node representing the argument to associate with the attribute; must be an expression
|
|
* @param m meta data to associate with the node
|
|
*/
|
|
static auto create(ASTContext* ctx, const attribute::Kind& kind, Expression* v, const Meta& m = Meta()) {
|
|
return ctx->make<Attribute>(ctx, {v}, kind, m);
|
|
}
|
|
|
|
/**
|
|
* Factory for an attribute with no argument.
|
|
*
|
|
* @param kind the attribute's internal representation
|
|
* @param m meta data to associate with the node
|
|
*/
|
|
static auto create(ASTContext* ctx, const attribute::Kind& kind, const Meta& m = Meta()) {
|
|
return create(ctx, kind, nullptr, m);
|
|
}
|
|
|
|
protected:
|
|
Attribute(ASTContext* ctx, Nodes children, attribute::Kind kind, Meta m = Meta())
|
|
: Node(ctx, NodeTags, std::move(children), std::move(m)), _kind(std::move(kind)) {}
|
|
|
|
std::string _dump() const override;
|
|
|
|
HILTI_NODE_0(Attribute, final);
|
|
|
|
private:
|
|
attribute::Kind _kind;
|
|
};
|
|
|
|
/** AST node holding a set of `Attribute` nodes. */
|
|
class AttributeSet : public Node {
|
|
public:
|
|
/** Returns the set's attributes. */
|
|
auto attributes() const { return children<Attribute>(0, {}); }
|
|
|
|
/**
|
|
* Retrieves an attribute with a given kind from the set. If multiple
|
|
* attributes with that kind exist, it's undefined which one is returned.
|
|
*
|
|
* @return attribute if found
|
|
*/
|
|
Attribute* find(const attribute::Kind& kind) const;
|
|
|
|
/**
|
|
* Retrieves all attributes with a given kind from the set.
|
|
*
|
|
* @return all attributes with matching kind
|
|
*/
|
|
hilti::node::Set<Attribute> findAll(const attribute::Kind& kind) const;
|
|
|
|
/**
|
|
* Returns true if there's an attribute with a given kind in the set.
|
|
*
|
|
* @param true if found
|
|
*/
|
|
bool has(const attribute::Kind& kind) const { return find(kind) != nullptr; }
|
|
|
|
/** Adds an attribute to the set. */
|
|
void add(ASTContext* ctx, Attribute* a) {
|
|
addChild(ctx, a);
|
|
// Combine this location with the attribute's location so this spans the range
|
|
setMeta(meta().mergeLocation(a->location()));
|
|
}
|
|
|
|
/** Removes all attributes of the given kind. */
|
|
void remove(const attribute::Kind& kind);
|
|
|
|
/** Returns true if the set has at least one element. */
|
|
operator bool() const { return ! attributes().empty(); }
|
|
|
|
static auto create(ASTContext* ctx, const Attributes& attrs = {}, Meta m = Meta()) {
|
|
return ctx->make<AttributeSet>(ctx, attrs, std::move(m));
|
|
}
|
|
|
|
protected:
|
|
/**
|
|
* Constructs a set from from a vector of attributes.
|
|
*
|
|
* @param a vector to initialize attribute set from
|
|
* @param m meta data to associate with the node
|
|
*/
|
|
explicit AttributeSet(ASTContext* ctx, Nodes children, Meta m = Meta())
|
|
: Node(ctx, NodeTags, std::move(children), std::move(m)) {}
|
|
|
|
/**
|
|
* Constructs an empty set.
|
|
*
|
|
* @param m meta data to associate with the node
|
|
*/
|
|
AttributeSet(ASTContext* ctx, Meta m = Meta()) : Node(ctx, {node::tag::AttributeSet}, {}, std::move(m)) {}
|
|
|
|
std::string _dump() const override;
|
|
|
|
HILTI_NODE_0(AttributeSet, final);
|
|
};
|
|
|
|
} // namespace hilti
|
|
|
|
namespace std {
|
|
template<>
|
|
struct hash<hilti::attribute::Kind> {
|
|
size_t operator()(const hilti::attribute::Kind& x) const { return std::hash<std::string>()(x); }
|
|
};
|
|
} // namespace std
|