Spicy
node.h
1 // Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <algorithm>
6 #include <cinttypes>
7 #include <iterator>
8 #include <map>
9 #include <memory>
10 #include <optional>
11 #include <ostream>
12 #include <ranges>
13 #include <string>
14 #include <unordered_set>
15 #include <utility>
16 #include <vector>
17 
18 #include <hilti/ast/doc-string.h>
19 #include <hilti/ast/forward.h>
20 #include <hilti/ast/id.h>
21 #include <hilti/ast/meta.h>
22 #include <hilti/ast/node-range.h>
23 #include <hilti/ast/node-tag.h>
24 #include <hilti/ast/scope.h>
25 #include <hilti/ast/visitor-dispatcher.h>
26 
27 #define __HILTI_NODE_COMMON_final(NS, CLASS) \
28  ::hilti::Node* _clone(::hilti::ASTContext* ctx) const final { return ctx->make<CLASS>(*this); }
29 
30 #define __HILTI_NODE_COMMON_override(NS, CLASS)
31 
32 #define __HILTI_NODE_COMMON(NS, CLASS, override_) \
33  friend class ::NS::builder::NodeBuilder; \
34  friend class hilti::ASTContext; \
35  friend class hilti::Node; \
36  std::string _typename() const override { return hilti::util::typename_(*this); } \
37  __HILTI_NODE_COMMON_##override_(NS, CLASS)
38 
39 #define __HILTI_NODE_0(NS, CLASS, override_) \
40  __HILTI_NODE_COMMON(NS, CLASS, override_) \
41  \
42  static constexpr uint16_t NodeLevel = 1; \
43  static constexpr ::hilti::node::Tag NodeTag = ::hilti::node::tag::CLASS; \
44  static constexpr ::hilti::node::Tags NodeTags = {::hilti::node::tag::Node, ::hilti::node::tag::CLASS};
45 
46 #define HILTI_NODE_0(CLASS, override_) \
47  __HILTI_NODE_0(hilti, CLASS, override_) \
48  \
49  void dispatch(::hilti::visitor::Dispatcher& v) override_ { \
50  v(static_cast<::hilti::Node*>(this)); \
51  v(this); \
52  }
53 
54 #define __HILTI_NODE_1(NS, CLASS, BASE, override_) \
55  __HILTI_NODE_COMMON(NS, CLASS, override_) \
56  \
57  static constexpr uint16_t NodeLevel = 2; \
58  static constexpr ::hilti::node::Tag NodeTag = ::hilti::node::tag::CLASS; \
59  static constexpr ::hilti::node::Tags NodeTags = {::hilti::node::tag::Node, \
60  ::hilti::node::tag::BASE, \
61  ::hilti::node::tag::CLASS};
62 
63 #define HILTI_NODE_1(CLASS, BASE, override_) \
64  __HILTI_NODE_1(hilti, CLASS, BASE, override_) \
65  \
66  void dispatch(::hilti::visitor::Dispatcher& v) override_ { \
67  v(static_cast<::hilti::Node*>(this)); \
68  v(static_cast<BASE*>(this)); \
69  v(this); \
70  }
71 
72 #define __HILTI_NODE_2(NS, CLASS, BASE1, BASE2, override_) \
73  __HILTI_NODE_COMMON(NS, CLASS, override_) \
74  \
75  static constexpr uint16_t NodeLevel = 3; \
76  static constexpr ::hilti::node::Tag NodeTag = ::hilti::node::tag::CLASS; \
77  static constexpr ::hilti::node::Tags NodeTags = {::hilti::node::tag::Node, \
78  ::hilti::node::tag::BASE2, \
79  ::hilti::node::tag::BASE1, \
80  ::hilti::node::tag::CLASS};
81 
82 #define HILTI_NODE_2(CLASS, BASE1, BASE2, override_) \
83  __HILTI_NODE_2(hilti, CLASS, BASE1, BASE2, override_) \
84  \
85  void dispatch(::hilti::visitor::Dispatcher& v) override_ { \
86  v(static_cast<::hilti::Node*>(this)); \
87  v(static_cast<BASE1*>(this)); \
88  v(static_cast<BASE2*>(this)); \
89  v(this); \
90  }
91 
92 namespace hilti {
93 namespace builder {
94 class NodeBuilder;
95 }
96 
97 namespace node {
98 
99 namespace detail {
101 Node* deepcopy(ASTContext* ctx, Node* n, bool force);
102 } // namespace detail
103 
113 template<typename T>
114 T* deepcopy(ASTContext* ctx, T* n, bool force = false) {
115  if ( ! n )
116  return nullptr;
117 
118  return detail::deepcopy(ctx, n, force)->template as<T>();
119 }
120 
122 using PropertyValue = std::
123  variant<bool, const char*, double, int, int64_t, unsigned int, uint64_t, std::string, ID, std::optional<uint64_t>>;
124 
126 inline std::string to_string(const PropertyValue& v) {
127  struct Visitor {
128  auto operator()(bool s) { return std::string(s ? "true" : "false"); }
129  auto operator()(const char* s) { return util::escapeUTF8(s); }
130  auto operator()(double d) { return util::fmt("%.6f", d); }
131  auto operator()(int i) { return util::fmt("%d", i); }
132  auto operator()(int64_t i) { return util::fmt("%" PRId64, i); }
133  auto operator()(const std::string& s) { return util::escapeUTF8(s); }
134  auto operator()(const ID& id) { return id.str(); }
135  auto operator()(const std::optional<uint64_t>& u) { return u ? util::fmt("%" PRIu64, *u) : "<not set>"; }
136  auto operator()(unsigned int u) { return util::fmt("%u", u); }
137  auto operator()(uint64_t u) { return util::fmt("%" PRIu64, u); }
138  };
139 
140  return std::visit(Visitor(), v);
141 };
142 
144 enum class ErrorPriority {
145  High = 3,
146  Normal = 2,
147  Low = 1,
148  NoError = 0
149 };
150 
151 inline bool operator<(ErrorPriority x, ErrorPriority y) {
152  return static_cast<std::underlying_type_t<ErrorPriority>>(x) <
153  static_cast<std::underlying_type_t<ErrorPriority>>(y);
154 }
155 
157 struct Error {
158  std::string message;
160  std::vector<std::string> context;
161  ErrorPriority priority = ErrorPriority::Normal;
163  // Comparison considers message & location, so that we can unique based
164  // on those two.
165  bool operator<(const Error& other) const {
166  return std::tie(message, location) < std::tie(other.message, other.location);
167  }
168 };
169 
175 using Properties = std::map<std::string, node::PropertyValue>;
176 
178 template<typename T>
179 class RetainedPtr {
180 public:
181  RetainedPtr() = default;
182  RetainedPtr(T* n) : _node(n) { _retain(); }
183  RetainedPtr(const RetainedPtr& other) : _node(other._node) { _retain(); }
184  RetainedPtr(RetainedPtr&& other) noexcept : _node(other._node) {
185  other._node = nullptr;
186  // reuse the other's retain
187  }
188 
189  ~RetainedPtr() { _release(); }
190 
191  RetainedPtr& operator=(const RetainedPtr& other) {
192  if ( this == &other )
193  return *this;
194 
195  _release();
196  _node = other._node;
197  _retain();
198  return *this;
199  }
200 
201  RetainedPtr& operator=(RetainedPtr&& other) noexcept {
202  if ( this == &other )
203  return *this;
204 
205  _release();
206  _node = other._node;
207  other._node = nullptr;
208  // reuse the other's retain
209  return *this;
210  }
211 
212  void reset() {
213  _release();
214  _node = nullptr;
215  }
216 
217  T* operator->() const { return _node; }
218  T& operator*() const { return *_node; }
219  explicit operator bool() const { return _node != nullptr; }
220  operator T*() const { return _node; }
221 
222  T* get() const { return _node; }
223 
224 private:
225  void _retain() {
226  if ( _node )
227  _node->retain();
228  }
229 
230  void _release() {
231  if ( _node ) {
232  _node->release();
233  _node = nullptr;
234  }
235  }
236 
237  T* _node = nullptr;
238 };
239 
240 } // namespace node
241 
243 class Node {
244 public:
245  virtual ~Node();
246 
248  node::Tag nodeTag() const {
249  // Get the last non-zero tag. The last element(s) may be unset
250  for ( auto _node_tag : std::ranges::reverse_view(_node_tags) ) {
251  if ( _node_tag != 0 )
252  return _node_tag;
253  }
254 
255  return 0;
256  }
257 
259  bool hasParent() const { return _parent; }
260 
267  Node* parent(int i = 1) const {
268  if ( i == 0 )
269  return nullptr;
270 
271  Node* p = _parent;
272  for ( ; p && i > 1; i-- )
273  p = p->_parent;
274 
275  return p;
276  }
277 
284  template<typename T>
285  T* parent() const {
286  if ( ! _parent )
287  return nullptr;
288  else if ( _parent->isA_<T>() )
289  return static_cast<T*>(_parent);
290  else
291  return _parent->parent<T>();
292  }
293 
294  /*
295  * Returns if this node matches the path in order of template types.
296  *
297  * @tparam Args the parent types to match, starting from immediate parent
298  * @return whether this node has parents of those types
299  */
300  template<typename... Args>
301  bool pathMatches() const {
302  const Node* current = this;
303  return (([&]() {
304  current = current->parent();
305  return current && current->isA<Args>();
306  }()) &&
307  ...);
308  }
309 
315  auto pathLength() const {
316  size_t i = 0;
317  for ( auto* n = parent(); n; i++, n = n->parent() )
318  ;
319 
320  return i;
321  }
322 
324  const auto& meta() const { return *_meta; }
325 
327  const auto& location() const { return _meta->location(); }
328 
330  void setMeta(Meta m) { _meta = Meta::get(std::move(m)); }
331 
336  auto scope() const { return _scope.get(); }
337 
344  if ( ! _scope )
345  _scope = std::make_unique<Scope>();
346 
347  return _scope.get();
348  }
349 
354  void clearScope() { _scope.reset(); }
355 
364  Result<std::pair<Declaration*, ID>> lookupID(const ID& id, const std::string_view& what) const;
365 
370  virtual bool inheritScope() const { return true; }
371 
376  std::string typename_() const { return _typename(); }
377 
379  uint64_t identity() const { return _identity; }
380 
382  const auto& children() const { return _children; }
383 
392  template<typename T>
393  T* child(int i) const {
394  if ( i < 0 )
395  i = static_cast<int>(_children.size()) + i;
396 
397  if ( std::cmp_greater_equal(i, _children.size()) )
398  return nullptr;
399 
400  return _children[i] ? _children[i]->as<T>() : nullptr;
401  }
402 
411  template<typename T>
412  T* childTryAs(int i) const {
413  if ( i < 0 )
414  i = static_cast<int>(_children.size()) + i;
415 
416  if ( std::cmp_greater_equal(i, _children.size()) )
417  return nullptr;
418 
419  return _children[i] ? _children[i]->tryAs<T>() : nullptr;
420  }
421 
429  Node* child(int i) const {
430  if ( i < 0 )
431  i = static_cast<int>(_children.size()) + i;
432 
433  if ( std::cmp_greater_equal(i, _children.size()) )
434  return nullptr;
435 
436  return _children[i];
437  }
438 
449  template<typename T>
450  auto children(int begin, std::optional<int> end) const {
451  end = _normalizeEndIndex(begin, end);
452  if ( end )
453  return hilti::node::Range<T>(_children.begin() + begin, _children.begin() + *end);
454  else
455  return hilti::node::Range<T>();
456  }
457 
468  template<typename T>
469  auto children(int begin, std::optional<int> end) {
470  end = _normalizeEndIndex(begin, end);
471  if ( end )
472  return hilti::node::Range<T>(_children.begin() + begin, _children.begin() + *end);
473  else
474  return hilti::node::Range<T>();
475  }
476 
483  template<typename T>
484  node::Set<T> childrenOfType() const {
485  typename hilti::node::Set<T> n;
486  for ( auto c = _children.begin(); c != _children.end(); c = std::next(c) ) {
487  if ( ! *c )
488  continue;
489 
490  if ( auto t = (*c)->tryAs<T>() )
491  n.push_back(t);
492  }
493 
494  return n;
495  }
496 
503  bool hasChild(const Node* n, bool recurse = false) const {
504  if ( ! n )
505  return false;
506 
507  if ( std::ranges::find(_children, n) != _children.end() )
508  return true;
509 
510  if ( ! recurse )
511  return false;
512 
513  for ( const auto* c : _children ) {
514  if ( c && c->hasChild(n, recurse) )
515  return true;
516  }
517 
518  return false;
519  }
520 
528  Node* sibling(Node* n) const {
529  auto i = std::ranges::find(_children, n);
530  if ( i == _children.end() )
531  return nullptr;
532 
533  while ( true ) {
534  if ( ++i == _children.end() )
535  return nullptr;
536 
537  if ( *i )
538  return *i;
539  }
540  }
541 
551  void addChild(ASTContext* ctx, Node* n) {
552  if ( ! n ) {
553  _children.emplace_back(nullptr);
554  return;
555  }
556 
557  n = _newChild(ctx, n);
558 
559  if ( ! n->location() && _meta && _meta->location() )
560  n->_meta = _meta;
561 
562  _children.emplace_back(n);
563  n->_parent = this;
564  n->retain();
565  }
566 
574  void addChildren(ASTContext* ctx, const Nodes& children) {
575  for ( auto&& n : children )
576  addChild(ctx, n);
577  }
578 
585  void removeChild(Node* n) {
586  if ( ! n )
587  return;
588 
589  if ( auto i = std::ranges::find(_children, n); i != _children.end() ) {
590  (*i)->_parent = nullptr;
591  (*i)->release();
592  _children.erase(i);
593  }
594  }
595 
604  void removeChildren(int begin, std::optional<int> end) {
605  end = _normalizeEndIndex(begin, end);
606  if ( ! end )
607  return;
608 
609  auto end_ = _children.begin() + *end;
610  for ( auto i = _children.begin() + begin; i < end_; i++ ) {
611  if ( *i ) {
612  (*i)->_parent = nullptr;
613  (*i)->release();
614  }
615  }
616 
617  _children.erase(_children.begin() + begin, end_);
618  }
619 
631  void setChild(ASTContext* ctx, size_t idx, Node* n) {
632  if ( auto* old = _children[idx] ) {
633  old->_parent = nullptr;
634  old->release();
635  }
636 
637  if ( ! n ) {
638  _children[idx] = nullptr;
639  return;
640  }
641 
642  n = _newChild(ctx, n);
643  n->_parent = this;
644  n->retain();
645 
646  if ( ! n->location() && _meta->location() )
647  n->_meta = _meta;
648 
649  _children[idx] = n;
650  }
651 
662  Node* clearChild(int i) {
663  if ( i < 0 )
664  i = static_cast<int>(_children.size()) + i;
665 
666  if ( std::cmp_greater_equal(i, _children.size()) )
667  return nullptr;
668 
669  auto* old = _children[i];
670  old->_parent = nullptr;
671  old->release();
672  _children[i] = nullptr;
673  return old;
674  }
675 
685  void replaceChildren(ASTContext* ctx, const Nodes& children);
686 
697  void replaceChild(ASTContext* ctx, Node* old, Node* new_);
698 
707 
709  template<typename T>
710  bool isA() const {
711 #ifndef NDEBUG
712  _checkCast<T>(false);
713 #endif
714  return (T::NodeLevel < _node_tags.size() && T::NodeTag == _node_tags[T::NodeLevel]);
715  }
716 
723  template<typename T>
724  bool isA_() const {
725  return (T::NodeLevel < _node_tags.size() && T::NodeTag == _node_tags[T::NodeLevel]);
726  }
727 
732  template<typename T>
733  T* as() const {
734 #ifndef NDEBUG
735  _checkCast<T>(true);
736 #endif
737  return static_cast<const T*>(this);
738  }
739 
746  template<typename T>
747  T* as() {
748 #ifndef NDEBUG
749  _checkCast<T>(true);
750 #endif
751  return static_cast<T*>(this);
752  }
753 
758  template<typename T>
759  const T* tryAs() const {
760 #ifndef NDEBUG
761  _checkCast<T>(false);
762 #endif
763 
764  if ( isA<T>() )
765  return static_cast<const T*>(this);
766  else
767  return nullptr;
768  }
769 
774  template<typename T>
775  T* tryAs() {
776 #ifndef NDEBUG
777  _checkCast<T>(false);
778 #endif
779  if ( isA<T>() )
780  return static_cast<T*>(this);
781  else
782  return nullptr;
783  }
784 
792  template<typename T>
793  T* tryAs_() {
794  if ( isA_<T>() )
795  return static_cast<T*>(this);
796  else
797  return nullptr;
798  }
799 
812  void print(std::ostream& out, bool compact, bool user_visible) const;
813 
821  std::string print() const;
822 
830  std::string printRaw() const;
831 
836  operator std::string() const { return print(); }
837 
842  std::string dump() const;
843 
851  std::string renderSelf(bool include_location = true) const;
852 
861  void addError(std::string msg, std::vector<std::string> context = {}) {
862  addError(std::move(msg), location(), std::move(context));
863  }
864 
874  void addError(std::string msg, node::ErrorPriority priority, std::vector<std::string> context = {}) {
875  addError(std::move(msg), location(), priority, std::move(context));
876  }
877 
886  void addError(std::string msg, const Location& /*l*/, std::vector<std::string> context = {}) {
887  addError(std::move(msg), location(), node::ErrorPriority::Normal, std::move(context));
888  }
889 
898  void addError(std::string msg, Location l, node::ErrorPriority priority, std::vector<std::string> context = {}) {
899  node::Error error;
900  error.message = std::move(msg);
901  error.location = std::move(l);
902  error.context = std::move(context);
903  error.priority = priority;
904 
905  if ( ! _errors )
906  _errors = std::make_unique<std::vector<node::Error>>();
907 
908  _errors->emplace_back(std::move(error));
909  }
910 
912  bool hasErrors() const { return _errors && ! _errors->empty(); }
913 
915  const auto& errors() const {
916  static std::vector<node::Error> no_errors;
917  return _errors ? *_errors : no_errors;
918  }
919 
921  void clearErrors() { _errors.reset(); }
922 
927  void clearChildren();
928 
930  void retain() {
931  assert(_ref_count != -1); // dtor sets ref count to -1
932  ++_ref_count;
933  }
934 
939  void release() {
940  assert(_ref_count != -1); // dtor sets ref count to -1
941  assert(_ref_count > 0);
942  --_ref_count;
943  }
944 
946  bool isRetained() const { return _ref_count > 0; }
947 
953  virtual node::Properties properties() const { return {}; }
954 
956  virtual void dispatch(visitor::Dispatcher& v) = 0;
957 
965  virtual std::string_view branchTag() const { return ""; }
966 
967  Node& operator=(const Node& other) = delete;
968  Node& operator=(Node&& other) noexcept = delete;
969 
970  static constexpr uint16_t NodeLevel = 0; // distance from `Node` in the inheritance hierarchy
971  static constexpr ::hilti::node::Tag NodeTag = ::hilti::node::tag::Node; // this class' node tag
972  static constexpr ::hilti::node::Tags NodeTags = {::hilti::node::tag::Node}; // this class' inheritance path
973 
974 protected:
984  Node(ASTContext* ctx, node::Tags node_tags, Nodes children, Meta meta)
985  : _node_tags(node_tags), _meta(Meta::get(std::move(meta))) {
986  assert(! _node_tags.empty());
987  _children.reserve(children.size());
988  for ( auto&& c : children ) {
989  if ( c ) {
990  c = _newChild(ctx, c);
991  assert(! c->_parent);
992  c->_parent = this;
993  c->retain();
994  }
995 
996  _children.push_back(c);
997  }
998  }
999 
1001  Node(ASTContext* /*ctx*/, node::Tags node_tags, Meta meta)
1002  : _node_tags(node_tags), _meta(Meta::get(std::move(meta))) {
1003  assert(! _node_tags.empty());
1004  }
1005 
1006  Node(Node&& other) = default;
1007 
1015  Node(const Node& other) : _node_tags(other._node_tags) {
1016  _meta = other._meta;
1017  _parent = nullptr;
1018 
1019  // Don't copy children. We can't copy the pointers because that would
1020  // produce messed up parent pointers. And we can't deep copy here
1021  // because we don't have the context. That's all ok because this isn't
1022  // public anyways; to run this one needs to go through Node's cloning
1023  // functions.
1024  }
1025 
1031  virtual std::string _typename() const { return util::typename_(*this); }
1032 
1039  virtual Node* _clone(ASTContext* ctx) const = 0; // shallow copy
1040 
1045  virtual std::string _dump() const { return ""; }
1046 
1047 private:
1048  friend Node* node::detail::deepcopy(ASTContext* ctx, Node* n, bool force);
1049 
1050  // Prepares a node for being added as a child, deep-copying it if it
1051  // already has a parent.
1052  static Node* _newChild(ASTContext* ctx, Node* child);
1053 
1054  // Do Python-style array indexing with negative indices.
1055  std::optional<int> _normalizeEndIndex(int begin, std::optional<int> end) const {
1056  if ( end && *end < 0 )
1057  end = static_cast<int>(_children.size()) + *end;
1058 
1059  if ( ! end )
1060  end = static_cast<int>(_children.size());
1061 
1062  if ( end > begin )
1063  return end;
1064  else {
1065  return {};
1066  }
1067  }
1068 
1069 #ifndef NDEBUG
1070  // Checks casts for common mistakes.
1071  //
1072  // @param enforce_success if true, we'll abort if the cast fails due to a type mismatch
1073  template<typename T>
1074  void _checkCast(bool enforce_success) const {
1075  // Ensure that our RTTI information yields the same `isA<>` result as
1076  // the C++ type system.
1077  auto ours = (T::NodeLevel < _node_tags.size() && T::NodeTag == _node_tags[T::NodeLevel]);
1078  auto theirs = (dynamic_cast<const T*>(this) != nullptr);
1079 
1080  if ( ours != theirs ) {
1081  std::cerr << util::fmt("internal error: Node::_checkCast() RTTI mismatch\n")
1082  << util::fmt("isA<T=%s>(%s) -> %s but dynamic_cast() says %s\n",
1083  typeid(T).name(),
1084  _typename(),
1085  ours ? "true" : "false",
1086  theirs ? "true" : "false")
1087  << util::fmt("T::type_level=%" PRIu16 " T::node_tags={%s} this->types={%s}\n",
1088  T::NodeLevel,
1089  node::to_string(T::NodeTags),
1090  node::to_string(_node_tags));
1091  abort();
1092  }
1093 
1094  if ( enforce_success && ! ours ) {
1095  std::cerr << util::fmt("internal error: unexpected type, want %s but have %s\n",
1096  util::typename_<T>(),
1097  _typename());
1098  abort();
1099  }
1100 
1101  // Debugging helper: If `this` is a `QualifiedType`, it's virtually
1102  // always wrong to try casting it into a class derived from
1103  // `UnqualifiedType`. Most likely that's meant to instead cast
1104  // `this->type()`. We abort here to make debugging such problems
1105  // easier.
1106  if ( std::is_base_of_v<UnqualifiedType, T> && ! std::is_same_v<UnqualifiedType, T> )
1107  _checkCastBackend();
1108  }
1109 #endif
1110 
1111  // Helper for _checkCast() to spot unexpected casts from `QualifiedType`.
1112  void _checkCastBackend() const;
1113 
1114  const node::Tags _node_tags; // inheritance path for the node
1115  int64_t _ref_count = 0; // number of pins currently held on the node; -1 is a special value set by the dtor to mark
1116  // an already destroyed node (for debugging)
1117  Node* _parent = nullptr; // parent node inside the AST, or null if not yet added to an AST
1118  Nodes _children; // set of child nodes
1119  const Meta* _meta; // meta information associated with the node; returned and managed by Meta::get()
1120 
1121  std::unique_ptr<Scope> _scope = nullptr; // scope associated with the node, or null if non (i.e., scope is empty)
1122  std::unique_ptr<std::vector<node::Error>> _errors; // errors associated with the node, or null if none
1123 
1124  static uint64_t _instances;
1125  uint64_t _identity = _instances++;
1126 };
1127 
1128 namespace node {
1129 
1135 public:
1140  ID uniqueID() const { return _id; }
1141 
1143  node::Properties properties() const { return {{"unique_id", _id}}; }
1144 
1145  WithUniqueID() = delete;
1146  WithUniqueID(const WithUniqueID& other) = default;
1147  WithUniqueID(WithUniqueID&& other) noexcept = default;
1148  ~WithUniqueID() = default;
1149 
1150  WithUniqueID& operator=(const WithUniqueID& other) = default;
1151  WithUniqueID& operator=(WithUniqueID&& other) noexcept = default;
1152 
1153 protected:
1161  WithUniqueID(const char* prefix) : _id(util::fmt("%s_%" PRIu64, prefix, _id_counter++)) {}
1162 
1163 private:
1164  ID _id;
1165 
1166  inline static uint64_t _id_counter = 0;
1167 };
1168 
1171 public:
1173  const std::optional<DocString>& documentation() const { return _doc; }
1174 
1176  void clearDocumentation() { _doc.reset(); }
1177 
1180  if ( doc )
1181  _doc = std::move(doc);
1182  else
1183  _doc.reset();
1184  }
1185 
1186 private:
1187  std::optional<DocString> _doc;
1188 };
1189 
1192 public:
1193  void recordSeen(const Node* n) { _seen.insert(n); }
1194  bool haveSeen(const Node* n) const { return _seen.contains(n); }
1195  void clear() { _seen.clear(); }
1196 
1197 private:
1198  std::unordered_set<const Node*> _seen;
1199 };
1200 
1205 template<typename T>
1206 Nodes flatten(std::vector<T> t) {
1207  Nodes v;
1208  v.reserve(t.size());
1209  for ( auto it = std::make_move_iterator(t.begin()); it != std::make_move_iterator(t.end()); ++it )
1210  v.emplace_back(*it);
1211 
1212  return v;
1213 }
1214 
1219 template<typename T>
1220 Nodes flatten(hilti::node::Range<T> t) {
1221  Nodes v;
1222  v.reserve(t.size());
1223  for ( const auto& i : t )
1224  v.emplace_back(std::move(i));
1225 
1226  return v;
1227 }
1228 
1230 template<typename T = Node*>
1231 inline Nodes flatten(Node* n) {
1232  return {n};
1233 }
1234 
1236 template<typename T>
1237 inline Nodes flatten(T* n) {
1238  return {std::move(n)};
1239 }
1240 
1242 template<typename T = std::nullptr_t>
1243 inline Nodes flatten(std::nullptr_t) {
1244  return {nullptr};
1245 }
1246 
1248 inline Nodes flatten() { return Nodes(); }
1249 
1254 template<typename T, typename... Ts>
1255 Nodes flatten(T t, Ts... ts)
1256  requires(0 != sizeof...(Ts))
1257 {
1258  return util::concat(std::move(flatten(std::move(t))), flatten(std::move(ts)...));
1259 }
1260 
1261 } // namespace node
1262 
1264 inline std::ostream& operator<<(std::ostream& out, const Node& n) {
1265  n.print(out, true, true);
1266  return out;
1267 }
1268 
1269 } // namespace hilti
1270 
1271 inline hilti::node::Properties operator+(hilti::node::Properties p1, hilti::node::Properties p2) {
1272  p1.merge(std::move(p2));
1273  return p1;
1274 }
1275 
1276 template<typename T>
1277 struct std::hash<hilti::node::RetainedPtr<T>> {
1278  size_t operator()(const hilti::node::RetainedPtr<T>& p) const { return std::hash<T*>()(p.get()); }
1279 };
Definition: ast-context.h:128
Definition: doc-string.h:15
Definition: id.h:15
Definition: location.h:17
Definition: meta.h:30
static const Meta * get(Meta m)
Definition: meta.h:77
Definition: node.h:243
Node * sibling(Node *n) const
Definition: node.h:528
T * parent() const
Definition: node.h:285
std::string renderSelf(bool include_location=true) const
Definition: node.cc:42
void clearScope()
Definition: node.h:354
T * tryAs_()
Definition: node.h:793
const T * tryAs() const
Definition: node.h:759
const auto & errors() const
Definition: node.h:915
void addError(std::string msg, std::vector< std::string > context={})
Definition: node.h:861
void setMeta(Meta m)
Definition: node.h:330
Node * clearChild(int i)
Definition: node.h:662
T * tryAs()
Definition: node.h:775
auto pathLength() const
Definition: node.h:315
T * as() const
Definition: node.h:733
bool hasErrors() const
Definition: node.h:912
bool isA_() const
Definition: node.h:724
auto children(int begin, std::optional< int > end) const
Definition: node.h:450
std::string print() const
Definition: node.cc:102
virtual Node * _clone(ASTContext *ctx) const =0
T * as()
Definition: node.h:747
Node * parent(int i=1) const
Definition: node.h:267
node::Tag nodeTag() const
Definition: node.h:248
void setChild(ASTContext *ctx, size_t idx, Node *n)
Definition: node.h:631
Node * removeFromParent()
Definition: node.cc:125
Node(const Node &other)
Definition: node.h:1015
void addError(std::string msg, const Location &, std::vector< std::string > context={})
Definition: node.h:886
void clearErrors()
Definition: node.h:921
void addChild(ASTContext *ctx, Node *n)
Definition: node.h:551
void removeChild(Node *n)
Definition: node.h:585
virtual std::string _typename() const
Definition: node.h:1031
void addError(std::string msg, Location l, node::ErrorPriority priority, std::vector< std::string > context={})
Definition: node.h:898
virtual bool inheritScope() const
Definition: node.h:370
auto children(int begin, std::optional< int > end)
Definition: node.h:469
const auto & location() const
Definition: node.h:327
Node * child(int i) const
Definition: node.h:429
void release()
Definition: node.h:939
auto scope() const
Definition: node.h:336
const auto & children() const
Definition: node.h:382
auto getOrCreateScope()
Definition: node.h:343
virtual std::string_view branchTag() const
Definition: node.h:965
void replaceChild(ASTContext *ctx, Node *old, Node *new_)
Definition: node.cc:114
void addError(std::string msg, node::ErrorPriority priority, std::vector< std::string > context={})
Definition: node.h:874
uint64_t identity() const
Definition: node.h:379
void replaceChildren(ASTContext *ctx, const Nodes &children)
Definition: node.cc:134
const auto & meta() const
Definition: node.h:324
bool hasChild(const Node *n, bool recurse=false) const
Definition: node.h:503
T * childTryAs(int i) const
Definition: node.h:412
virtual std::string _dump() const
Definition: node.h:1045
Node(ASTContext *ctx, node::Tags node_tags, Nodes children, Meta meta)
Definition: node.h:984
void removeChildren(int begin, std::optional< int > end)
Definition: node.h:604
bool isRetained() const
Definition: node.h:946
node::Set< T > childrenOfType() const
Definition: node.h:484
std::string printRaw() const
Definition: node.cc:108
std::string dump() const
Definition: node.cc:35
void clearChildren()
Definition: node.cc:141
bool hasParent() const
Definition: node.h:259
virtual node::Properties properties() const
Definition: node.h:953
virtual void dispatch(visitor::Dispatcher &v)=0
void addChildren(ASTContext *ctx, const Nodes &children)
Definition: node.h:574
T * child(int i) const
Definition: node.h:393
Node(ASTContext *, node::Tags node_tags, Meta meta)
Definition: node.h:1001
bool isA() const
Definition: node.h:710
void retain()
Definition: node.h:930
Result< std::pair< Declaration *, ID > > lookupID(const ID &id, const std::string_view &what) const
Definition: node.cc:227
std::string typename_() const
Definition: node.h:376
Definition: forward.h:757
Definition: node.h:1191
Definition: node-range.h:92
Definition: node.h:179
Definition: node.h:1170
void clearDocumentation()
Definition: node.h:1176
const std::optional< DocString > & documentation() const
Definition: node.h:1173
void setDocumentation(DocString doc)
Definition: node.h:1179
Definition: node.h:1134
ID uniqueID() const
Definition: node.h:1140
node::Properties properties() const
Definition: node.h:1143
WithUniqueID(const char *prefix)
Definition: node.h:1161
Definition: result.h:73
Definition: visitor-dispatcher.h:9
Definition: node.h:157
std::string message
Definition: node.h:158
ErrorPriority priority
Definition: node.h:161
Location location
Definition: node.h:159
std::vector< std::string > context
Definition: node.h:160