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, ::hilti::node::tag::BASE, \
60  ::hilti::node::tag::CLASS};
61 
62 #define HILTI_NODE_1(CLASS, BASE, override_) \
63  __HILTI_NODE_1(hilti, CLASS, BASE, override_) \
64  \
65  void dispatch(::hilti::visitor::Dispatcher& v) override_ { \
66  v(static_cast<::hilti::Node*>(this)); \
67  v(static_cast<BASE*>(this)); \
68  v(this); \
69  }
70 
71 #define __HILTI_NODE_2(NS, CLASS, BASE1, BASE2, override_) \
72  __HILTI_NODE_COMMON(NS, CLASS, override_) \
73  \
74  static constexpr uint16_t NodeLevel = 3; \
75  static constexpr ::hilti::node::Tag NodeTag = ::hilti::node::tag::CLASS; \
76  static constexpr ::hilti::node::Tags NodeTags = {::hilti::node::tag::Node, ::hilti::node::tag::BASE2, \
77  ::hilti::node::tag::BASE1, ::hilti::node::tag::CLASS};
78 
79 #define HILTI_NODE_2(CLASS, BASE1, BASE2, override_) \
80  __HILTI_NODE_2(hilti, CLASS, BASE1, BASE2, override_) \
81  \
82  void dispatch(::hilti::visitor::Dispatcher& v) override_ { \
83  v(static_cast<::hilti::Node*>(this)); \
84  v(static_cast<BASE1*>(this)); \
85  v(static_cast<BASE2*>(this)); \
86  v(this); \
87  }
88 
89 namespace hilti {
90 namespace builder {
91 class NodeBuilder;
92 }
93 
94 namespace node {
95 
96 namespace detail {
98 Node* deepcopy(ASTContext* ctx, Node* n, bool force);
99 } // namespace detail
100 
110 template<typename T>
111 T* deepcopy(ASTContext* ctx, T* n, bool force = false) {
112  if ( ! n )
113  return nullptr;
114 
115  return detail::deepcopy(ctx, n, force)->template as<T>();
116 }
117 
119 using PropertyValue = std::variant<bool, const char*, double, int, int64_t, unsigned int, uint64_t, std::string, ID,
120  std::optional<uint64_t>>;
121 
123 inline std::string to_string(const PropertyValue& v) {
124  struct Visitor {
125  auto operator()(bool s) { return std::string(s ? "true" : "false"); }
126  auto operator()(const char* s) { return util::escapeUTF8(s); }
127  auto operator()(double d) { return util::fmt("%.6f", d); }
128  auto operator()(int i) { return util::fmt("%d", i); }
129  auto operator()(int64_t i) { return util::fmt("%" PRId64, i); }
130  auto operator()(const std::string& s) { return util::escapeUTF8(s); }
131  auto operator()(const ID& id) { return id.str(); }
132  auto operator()(const std::optional<uint64_t>& u) { return u ? util::fmt("%" PRIu64, *u) : "<not set>"; }
133  auto operator()(unsigned int u) { return util::fmt("%u", u); }
134  auto operator()(uint64_t u) { return util::fmt("%" PRIu64, u); }
135  };
136 
137  return std::visit(Visitor(), v);
138 };
139 
141 enum class ErrorPriority {
142  High = 3,
143  Normal = 2,
144  Low = 1,
145  NoError = 0
146 };
147 
148 inline bool operator<(ErrorPriority x, ErrorPriority y) {
149  return static_cast<std::underlying_type_t<ErrorPriority>>(x) <
150  static_cast<std::underlying_type_t<ErrorPriority>>(y);
151 }
152 
154 struct Error {
155  std::string message;
157  std::vector<std::string> context;
158  ErrorPriority priority = ErrorPriority::Normal;
160  // Comparison considers message & location, so that we can unique based
161  // on those two.
162  bool operator<(const Error& other) const {
163  return std::tie(message, location) < std::tie(other.message, other.location);
164  }
165 };
166 
172 using Properties = std::map<std::string, node::PropertyValue>;
173 
175 template<typename T>
176 class RetainedPtr {
177 public:
178  RetainedPtr() = default;
179  RetainedPtr(T* n) : _node(n) { retain(); }
180  RetainedPtr(const RetainedPtr& other) : _node(other._node) { retain(); }
181  RetainedPtr(RetainedPtr&& other) noexcept : _node(other._node) {
182  other._node = nullptr;
183  // reuse the other's retain
184  }
185 
186  ~RetainedPtr() { release(); }
187 
188  RetainedPtr& operator=(const RetainedPtr& other) {
189  if ( this == &other )
190  return *this;
191 
192  release();
193  _node = other._node;
194  retain();
195  return *this;
196  }
197 
198  RetainedPtr& operator=(RetainedPtr&& other) noexcept {
199  if ( this == &other )
200  return *this;
201 
202  release();
203  _node = other._node;
204  other._node = nullptr;
205  // reuse the other's retain
206  return *this;
207  }
208 
209  void reset() {
210  release();
211  _node = nullptr;
212  }
213 
214  T* operator->() const { return _node; }
215  T& operator*() const { return *_node; }
216  explicit operator bool() const { return _node != nullptr; }
217  operator T*() const { return _node; }
218 
219  T* get() const { return _node; }
220 
221 private:
222  void retain() {
223  if ( _node )
224  _node->retain();
225  }
226 
227  void release() {
228  if ( _node ) {
229  _node->release();
230  _node = nullptr;
231  }
232  }
233 
234  T* _node = nullptr;
235 };
236 
237 } // namespace node
238 
240 class Node {
241 public:
242  virtual ~Node();
243 
245  node::Tag nodeTag() const {
246  // Get the last non-zero tag. The last element(s) may be unset
247  for ( auto _node_tag : std::ranges::reverse_view(_node_tags) ) {
248  if ( _node_tag != 0 )
249  return _node_tag;
250  }
251 
252  return 0;
253  }
254 
256  bool hasParent() const { return _parent; }
257 
264  Node* parent(int i = 1) const {
265  if ( i == 0 )
266  return nullptr;
267 
268  Node* p = _parent;
269  for ( ; p && i > 1; i-- )
270  p = p->_parent;
271 
272  return p;
273  }
274 
281  template<typename T>
282  T* parent() const {
283  if ( ! _parent )
284  return nullptr;
285  else if ( _parent->isA_<T>() )
286  return static_cast<T*>(_parent);
287  else
288  return _parent->parent<T>();
289  }
290 
296  auto pathLength() const {
297  size_t i = 0;
298  for ( auto* n = parent(); n; i++, n = n->parent() )
299  ;
300 
301  return i;
302  }
303 
304 
306  const auto& meta() const { return *_meta; }
307 
309  const auto& location() const { return _meta->location(); }
310 
312  void setMeta(Meta m) { _meta = Meta::get(std::move(m)); }
313 
318  auto scope() const { return _scope.get(); }
319 
326  if ( ! _scope )
327  _scope = std::make_unique<Scope>();
328 
329  return _scope.get();
330  }
331 
336  void clearScope() { _scope.reset(); }
337 
346  Result<std::pair<Declaration*, ID>> lookupID(const ID& id, const std::string_view& what) const;
347 
352  virtual bool inheritScope() const { return true; }
353 
358  std::string typename_() const { return _typename(); }
359 
361  uint64_t identity() const { return _identity; }
362 
364  const auto& children() const { return _children; }
365 
373  template<typename T>
374  T* child(unsigned int i) const {
375  if ( i >= _children.size() )
376  return nullptr;
377 
378  return _children[i] ? _children[i]->as<T>() : nullptr;
379  }
380 
388  template<typename T>
389  T* childTryAs(unsigned int i) const {
390  if ( i >= _children.size() )
391  return nullptr;
392 
393  return _children[i] ? _children[i]->tryAs<T>() : nullptr;
394  }
395 
403  Node* child(unsigned int i) const {
404  if ( i >= _children.size() )
405  return nullptr;
406 
407  return _children[i];
408  }
409 
420  template<typename T>
421  auto children(int begin, std::optional<int> end) const {
422  end = _normalizeEndIndex(begin, end);
423  if ( end )
424  return hilti::node::Range<T>(_children.begin() + begin, _children.begin() + *end);
425  else
426  return hilti::node::Range<T>();
427  }
428 
439  template<typename T>
440  auto children(int begin, std::optional<int> end) {
441  end = _normalizeEndIndex(begin, end);
442  if ( end )
443  return hilti::node::Range<T>(_children.begin() + begin, _children.begin() + *end);
444  else
445  return hilti::node::Range<T>();
446  }
447 
454  template<typename T>
455  node::Set<T> childrenOfType() const {
456  typename hilti::node::Set<T> n;
457  for ( auto c = _children.begin(); c != _children.end(); c = std::next(c) ) {
458  if ( ! *c )
459  continue;
460 
461  if ( auto t = (*c)->tryAs<T>() )
462  n.push_back(t);
463  }
464 
465  return n;
466  }
467 
474  bool hasChild(const Node* n, bool recurse = false) const {
475  if ( ! n )
476  return false;
477 
478  if ( std::ranges::find(_children, n) != _children.end() )
479  return true;
480 
481  if ( ! recurse )
482  return false;
483 
484  for ( const auto* c : _children ) {
485  if ( c && c->hasChild(n, recurse) )
486  return true;
487  }
488 
489  return false;
490  }
491 
499  Node* sibling(Node* n) const {
500  auto i = std::ranges::find(_children, n);
501  if ( i == _children.end() )
502  return nullptr;
503 
504  while ( true ) {
505  if ( ++i == _children.end() )
506  return nullptr;
507 
508  if ( *i )
509  return *i;
510  }
511  }
512 
522  void addChild(ASTContext* ctx, Node* n) {
523  if ( ! n ) {
524  _children.emplace_back(nullptr);
525  return;
526  }
527 
528  n = _newChild(ctx, n);
529 
530  if ( ! n->location() && _meta->location() )
531  n->_meta = _meta;
532 
533  _children.emplace_back(n);
534  n->_parent = this;
535  n->retain();
536  }
537 
545  void addChildren(ASTContext* ctx, const Nodes& children) {
546  for ( auto&& n : children )
547  addChild(ctx, n);
548  }
549 
556  void removeChild(Node* n) {
557  if ( ! n )
558  return;
559 
560  if ( auto i = std::ranges::find(_children, n); i != _children.end() ) {
561  (*i)->_parent = nullptr;
562  (*i)->release();
563  _children.erase(i);
564  }
565  }
566 
575  void removeChildren(int begin, std::optional<int> end) {
576  end = _normalizeEndIndex(begin, end);
577  if ( ! end )
578  return;
579 
580  auto end_ = _children.begin() + *end;
581  for ( auto i = _children.begin() + begin; i < end_; i++ ) {
582  if ( *i ) {
583  (*i)->_parent = nullptr;
584  (*i)->release();
585  }
586  }
587 
588  _children.erase(_children.begin() + begin, end_);
589  }
590 
602  void setChild(ASTContext* ctx, size_t idx, Node* n) {
603  if ( auto* old = _children[idx] ) {
604  old->_parent = nullptr;
605  old->release();
606  }
607 
608  if ( ! n ) {
609  _children[idx] = nullptr;
610  return;
611  }
612 
613  n = _newChild(ctx, n);
614  n->_parent = this;
615  n->retain();
616 
617  if ( ! n->location() && _meta->location() )
618  n->_meta = _meta;
619 
620  _children[idx] = n;
621  }
622 
632  void replaceChildren(ASTContext* ctx, const Nodes& children);
633 
644  void replaceChild(ASTContext* ctx, Node* old, Node* new_);
645 
647  template<typename T>
648  bool isA() const {
649 #ifndef NDEBUG
650  _checkCast<T>(false);
651 #endif
652  return (T::NodeLevel < _node_tags.size() && T::NodeTag == _node_tags[T::NodeLevel]);
653  }
654 
661  template<typename T>
662  bool isA_() const {
663  return (T::NodeLevel < _node_tags.size() && T::NodeTag == _node_tags[T::NodeLevel]);
664  }
665 
670  template<typename T>
671  T* as() const {
672 #ifndef NDEBUG
673  _checkCast<T>(true);
674 #endif
675  return static_cast<const T*>(this);
676  }
677 
684  template<typename T>
685  T* as() {
686 #ifndef NDEBUG
687  _checkCast<T>(true);
688 #endif
689  return static_cast<T*>(this);
690  }
691 
696  template<typename T>
697  const T* tryAs() const {
698 #ifndef NDEBUG
699  _checkCast<T>(false);
700 #endif
701 
702  if ( isA<T>() )
703  return static_cast<const T*>(this);
704  else
705  return nullptr;
706  }
707 
712  template<typename T>
713  T* tryAs() {
714 #ifndef NDEBUG
715  _checkCast<T>(false);
716 #endif
717  if ( isA<T>() )
718  return static_cast<T*>(this);
719  else
720  return nullptr;
721  }
722 
730  template<typename T>
731  T* tryAs_() {
732  if ( isA_<T>() )
733  return static_cast<T*>(this);
734  else
735  return nullptr;
736  }
737 
750  void print(std::ostream& out, bool compact, bool user_visible) const;
751 
759  std::string print() const;
760 
768  std::string printRaw() const;
769 
774  operator std::string() const { return print(); }
775 
780  std::string dump() const;
781 
789  std::string renderSelf(bool include_location = true) const;
790 
799  void addError(std::string msg, std::vector<std::string> context = {}) {
800  addError(std::move(msg), location(), std::move(context));
801  }
802 
812  void addError(std::string msg, node::ErrorPriority priority, std::vector<std::string> context = {}) {
813  addError(std::move(msg), location(), priority, std::move(context));
814  }
815 
824  void addError(std::string msg, const Location& l, std::vector<std::string> context = {}) {
825  addError(std::move(msg), location(), node::ErrorPriority::Normal, std::move(context));
826  }
827 
836  void addError(std::string msg, Location l, node::ErrorPriority priority, std::vector<std::string> context = {}) {
837  node::Error error;
838  error.message = std::move(msg);
839  error.location = std::move(l);
840  error.context = std::move(context);
841  error.priority = priority;
842 
843  if ( ! _errors )
844  _errors = std::make_unique<std::vector<node::Error>>();
845 
846  _errors->emplace_back(std::move(error));
847  }
848 
850  bool hasErrors() const { return _errors && ! _errors->empty(); }
851 
853  const auto& errors() const {
854  static std::vector<node::Error> no_errors;
855  return _errors ? *_errors : no_errors;
856  }
857 
859  void clearErrors() { _errors.reset(); }
860 
865  void clearChildren();
866 
868  void retain() {
869  assert(_ref_count != -1); // dtor sets ref count to -1
870  ++_ref_count;
871  }
872 
877  void release() {
878  assert(_ref_count != -1); // dtor sets ref count to -1
879  assert(_ref_count > 0);
880  --_ref_count;
881  }
882 
884  bool isRetained() const { return _ref_count > 0; }
885 
891  virtual node::Properties properties() const { return {}; }
892 
894  virtual void dispatch(visitor::Dispatcher& v) = 0;
895 
903  virtual std::string_view branchTag() const { return ""; }
904 
905  Node& operator=(const Node& other) = delete;
906  Node& operator=(Node&& other) noexcept = delete;
907 
908  static constexpr uint16_t NodeLevel = 0; // distance from `Node` in the inheritance hierarchy
909  static constexpr ::hilti::node::Tag NodeTag = ::hilti::node::tag::Node; // this class' node tag
910  static constexpr ::hilti::node::Tags NodeTags = {::hilti::node::tag::Node}; // this class' inheritance path
911 
912 protected:
922  Node(ASTContext* ctx, node::Tags node_tags, Nodes children, Meta meta)
923  : _node_tags(node_tags), _meta(Meta::get(std::move(meta))) {
924  assert(! _node_tags.empty());
925  _children.reserve(children.size());
926  for ( auto&& c : children ) {
927  if ( c ) {
928  c = _newChild(ctx, c);
929  assert(! c->_parent);
930  c->_parent = this;
931  c->retain();
932  }
933 
934  _children.push_back(c);
935  }
936  }
937 
939  Node(ASTContext* ctx, node::Tags node_tags, Meta meta) : _node_tags(node_tags), _meta(Meta::get(std::move(meta))) {
940  assert(! _node_tags.empty());
941  }
942 
943  Node(Node&& other) = default;
944 
952  Node(const Node& other) : _node_tags(other._node_tags) {
953  _meta = other._meta;
954  _parent = nullptr;
955 
956  // Don't copy children. We can't copy the pointers because that would
957  // produce messed up parent pointers. And we can't deep copy here
958  // because we don't have the context. That's all ok because this isn't
959  // public anyways; to run this one needs to go through Node's cloning
960  // functions.
961  }
962 
968  virtual std::string _typename() const { return util::typename_(*this); }
969 
976  virtual Node* _clone(ASTContext* ctx) const = 0; // shallow copy
977 
982  virtual std::string _dump() const { return ""; }
983 
984 private:
985  friend Node* node::detail::deepcopy(ASTContext* ctx, Node* n, bool force);
986 
987  // Prepares a node for being added as a child, deep-copying it if it
988  // already has a parent.
989  static Node* _newChild(ASTContext* ctx, Node* child);
990 
991  // Do Python-style array indexing with negative indices.
992  std::optional<int> _normalizeEndIndex(int begin, std::optional<int> end) const {
993  if ( end && *end < 0 )
994  end = static_cast<int>(_children.size()) + *end;
995 
996  if ( ! end )
997  end = _children.size();
998 
999  if ( end > begin )
1000  return end;
1001  else {
1002  return {};
1003  }
1004  }
1005 
1006 #ifndef NDEBUG
1007  // Checks casts for common mistakes.
1008  //
1009  // @param enforce_success if true, we'll abort if the cast fails due to a type mismatch
1010  template<typename T>
1011  void _checkCast(bool enforce_success) const {
1012  // Ensure that our RTTI information yields the same `isA<>` result as
1013  // the C++ type system.
1014  auto ours = (T::NodeLevel < _node_tags.size() && T::NodeTag == _node_tags[T::NodeLevel]);
1015  auto theirs = (dynamic_cast<const T*>(this) != nullptr);
1016 
1017  if ( ours != theirs ) {
1018  std::cerr << util::fmt("internal error: Node::_checkCast() RTTI mismatch\n")
1019  << util::fmt("isA<T=%s>(%s) -> %s but dynamic_cast() says %s\n", typeid(T).name(), _typename(),
1020  ours ? "true" : "false", theirs ? "true" : "false")
1021  << util::fmt("T::type_level=%" PRIu16 " T::node_tags={%s} this->types={%s}\n", T::NodeLevel,
1022  node::to_string(T::NodeTags), node::to_string(_node_tags));
1023  abort();
1024  }
1025 
1026  if ( enforce_success && ! ours ) {
1027  std::cerr << util::fmt("internal error: unexpected type, want %s but have %s\n", util::typename_<T>(),
1028  _typename());
1029  abort();
1030  }
1031 
1032  // Debugging helper: If `this` is a `QualifiedType`, it's virtually
1033  // always wrong to try casting it into a class derived from
1034  // `UnqualifiedType`. Most likely that's meant to instead cast
1035  // `this->type()`. We abort here to make debugging such problems
1036  // easier.
1037  if ( std::is_base_of_v<UnqualifiedType, T> && ! std::is_same_v<UnqualifiedType, T> )
1038  _checkCastBackend();
1039  }
1040 #endif
1041 
1042  // Helper for _checkCast() to spot unexpected casts from `QualifiedType`.
1043  void _checkCastBackend() const;
1044 
1045  const node::Tags _node_tags; // inheritance path for the node
1046  int64_t _ref_count = 0; // number of pins currently held on the node; -1 is a special value set by the dtor to mark
1047  // an already destroyed node (for debugging)
1048  Node* _parent = nullptr; // parent node inside the AST, or null if not yet added to an AST
1049  Nodes _children; // set of child nodes
1050  const Meta* _meta; // meta information associated with the node; returned and managed by Meta::get()
1051 
1052  std::unique_ptr<Scope> _scope = nullptr; // scope associated with the node, or null if non (i.e., scope is empty)
1053  std::unique_ptr<std::vector<node::Error>> _errors; // errors associated with the node, or null if none
1054 
1055  static uint64_t _instances;
1056  uint64_t _identity = _instances++;
1057 };
1058 
1059 namespace node {
1060 
1066 public:
1071  ID uniqueID() const { return _id; }
1072 
1074  node::Properties properties() const { return {{"unique_id", _id}}; }
1075 
1076  WithUniqueID() = delete;
1077  WithUniqueID(const WithUniqueID& other) = default;
1078  WithUniqueID(WithUniqueID&& other) noexcept = default;
1079  ~WithUniqueID() = default;
1080 
1081  WithUniqueID& operator=(const WithUniqueID& other) = default;
1082  WithUniqueID& operator=(WithUniqueID&& other) noexcept = default;
1083 
1084 protected:
1092  WithUniqueID(const char* prefix) : _id(util::fmt("%s_%" PRIu64, prefix, _id_counter++)) {}
1093 
1094 private:
1095  ID _id;
1096 
1097  inline static uint64_t _id_counter = 0;
1098 };
1099 
1102 public:
1104  const std::optional<DocString>& documentation() const { return _doc; }
1105 
1107  void clearDocumentation() { _doc.reset(); }
1108 
1111  if ( doc )
1112  _doc = std::move(doc);
1113  else
1114  _doc.reset();
1115  }
1116 
1117 private:
1118  std::optional<DocString> _doc;
1119 };
1120 
1123 public:
1124  void recordSeen(const Node* n) { _seen.insert(n); }
1125  bool haveSeen(const Node* n) const { return _seen.contains(n); }
1126  void clear() { _seen.clear(); }
1127 
1128 private:
1129  std::unordered_set<const Node*> _seen;
1130 };
1131 
1136 template<typename T>
1137 Nodes flatten(std::vector<T> t) {
1138  Nodes v;
1139  v.reserve(t.size());
1140  for ( auto it = std::make_move_iterator(t.begin()); it != std::make_move_iterator(t.end()); ++it )
1141  v.emplace_back(*it);
1142 
1143  return v;
1144 }
1145 
1150 template<typename T>
1151 Nodes flatten(hilti::node::Range<T> t) {
1152  Nodes v;
1153  v.reserve(t.size());
1154  for ( const auto& i : t )
1155  v.emplace_back(std::move(i));
1156 
1157  return v;
1158 }
1159 
1161 template<typename T = Node*>
1162 inline Nodes flatten(Node* n) {
1163  return {n};
1164 }
1165 
1167 template<typename T>
1168 inline Nodes flatten(T* n) {
1169  return {std::move(n)};
1170 }
1171 
1173 template<typename T = std::nullptr_t>
1174 inline Nodes flatten(std::nullptr_t) {
1175  return {nullptr};
1176 }
1177 
1179 inline Nodes flatten() { return Nodes(); }
1180 
1185 template<typename T, typename... Ts>
1186 Nodes flatten(T t, Ts... ts)
1187  requires(0 != sizeof...(Ts))
1188 {
1189  return util::concat(std::move(flatten(std::move(t))), flatten(std::move(ts)...));
1190 }
1191 
1196 template<typename X, typename F>
1197 auto filter(const hilti::node::Range<X>& x, F f) {
1198  hilti::node::Set<X> y;
1199  for ( const auto& i : x ) {
1200  if ( f(i) )
1201  y.push_back(i);
1202  }
1203 
1204  return y;
1205 }
1206 
1211 template<typename X, typename F>
1212 auto filter(const hilti::node::Set<X>& x, F f) {
1213  hilti::node::Set<X> y;
1214  for ( const auto& i : x ) {
1215  if ( f(i) )
1216  y.push_back(i);
1217  }
1218 
1219  return y;
1220 }
1221 
1226 template<typename X, typename F>
1227 auto transform(const hilti::node::Range<X>& x, F f) {
1228  using Y = std::invoke_result_t<F, X*>;
1229  std::vector<Y> y;
1230  y.reserve(x.size());
1231  for ( const auto& i : x )
1232  y.push_back(f(i));
1233 
1234  return y;
1235 }
1236 
1241 template<typename X, typename F>
1242 auto transform(const hilti::node::Set<X>& x, F f) {
1243  using Y = std::invoke_result_t<F, X*>;
1244  std::vector<Y> y;
1245  y.reserve(x.size());
1246  for ( const auto& i : x )
1247  y.push_back(f(i));
1248 
1249  return y;
1250 }
1251 
1252 } // namespace node
1253 
1255 inline std::ostream& operator<<(std::ostream& out, const Node& n) {
1256  n.print(out, true, true);
1257  return out;
1258 }
1259 
1260 } // namespace hilti
1261 
1262 inline hilti::node::Properties operator+(hilti::node::Properties p1, hilti::node::Properties p2) {
1263  p1.merge(std::move(p2));
1264  return p1;
1265 }
Definition: ast-context.h:121
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:240
Node * sibling(Node *n) const
Definition: node.h:499
T * parent() const
Definition: node.h:282
std::string renderSelf(bool include_location=true) const
Definition: node.cc:41
void clearScope()
Definition: node.h:336
T * tryAs_()
Definition: node.h:731
const T * tryAs() const
Definition: node.h:697
const auto & errors() const
Definition: node.h:853
void addError(std::string msg, std::vector< std::string > context={})
Definition: node.h:799
void setMeta(Meta m)
Definition: node.h:312
T * tryAs()
Definition: node.h:713
auto pathLength() const
Definition: node.h:296
T * as() const
Definition: node.h:671
bool hasErrors() const
Definition: node.h:850
bool isA_() const
Definition: node.h:662
auto children(int begin, std::optional< int > end) const
Definition: node.h:421
std::string print() const
Definition: node.cc:101
virtual Node * _clone(ASTContext *ctx) const =0
T * as()
Definition: node.h:685
Node * parent(int i=1) const
Definition: node.h:264
node::Tag nodeTag() const
Definition: node.h:245
void setChild(ASTContext *ctx, size_t idx, Node *n)
Definition: node.h:602
Node(const Node &other)
Definition: node.h:952
void clearErrors()
Definition: node.h:859
void addChild(ASTContext *ctx, Node *n)
Definition: node.h:522
void removeChild(Node *n)
Definition: node.h:556
virtual std::string _typename() const
Definition: node.h:968
T * childTryAs(unsigned int i) const
Definition: node.h:389
void addError(std::string msg, Location l, node::ErrorPriority priority, std::vector< std::string > context={})
Definition: node.h:836
virtual bool inheritScope() const
Definition: node.h:352
auto children(int begin, std::optional< int > end)
Definition: node.h:440
const auto & location() const
Definition: node.h:309
void release()
Definition: node.h:877
auto scope() const
Definition: node.h:318
const auto & children() const
Definition: node.h:364
auto getOrCreateScope()
Definition: node.h:325
virtual std::string_view branchTag() const
Definition: node.h:903
void replaceChild(ASTContext *ctx, Node *old, Node *new_)
Definition: node.cc:113
Node * child(unsigned int i) const
Definition: node.h:403
void addError(std::string msg, node::ErrorPriority priority, std::vector< std::string > context={})
Definition: node.h:812
uint64_t identity() const
Definition: node.h:361
void replaceChildren(ASTContext *ctx, const Nodes &children)
Definition: node.cc:124
const auto & meta() const
Definition: node.h:306
bool hasChild(const Node *n, bool recurse=false) const
Definition: node.h:474
virtual std::string _dump() const
Definition: node.h:982
Node(ASTContext *ctx, node::Tags node_tags, Nodes children, Meta meta)
Definition: node.h:922
void removeChildren(int begin, std::optional< int > end)
Definition: node.h:575
bool isRetained() const
Definition: node.h:884
T * child(unsigned int i) const
Definition: node.h:374
node::Set< T > childrenOfType() const
Definition: node.h:455
std::string printRaw() const
Definition: node.cc:107
std::string dump() const
Definition: node.cc:34
Node(ASTContext *ctx, node::Tags node_tags, Meta meta)
Definition: node.h:939
void clearChildren()
Definition: node.cc:131
bool hasParent() const
Definition: node.h:256
virtual node::Properties properties() const
Definition: node.h:891
virtual void dispatch(visitor::Dispatcher &v)=0
void addChildren(ASTContext *ctx, const Nodes &children)
Definition: node.h:545
bool isA() const
Definition: node.h:648
void retain()
Definition: node.h:868
Result< std::pair< Declaration *, ID > > lookupID(const ID &id, const std::string_view &what) const
Definition: node.cc:217
std::string typename_() const
Definition: node.h:358
void addError(std::string msg, const Location &l, std::vector< std::string > context={})
Definition: node.h:824
Definition: forward.h:758
Definition: node.h:1122
Definition: node-range.h:92
Definition: node.h:176
Definition: node.h:1101
void clearDocumentation()
Definition: node.h:1107
const std::optional< DocString > & documentation() const
Definition: node.h:1104
void setDocumentation(DocString doc)
Definition: node.h:1110
Definition: node.h:1065
ID uniqueID() const
Definition: node.h:1071
node::Properties properties() const
Definition: node.h:1074
WithUniqueID(const char *prefix)
Definition: node.h:1092
Definition: result.h:71
Definition: visitor-dispatcher.h:9
auto transform(const C &x, F f)
Definition: util.h:419
Definition: node.h:154
std::string message
Definition: node.h:155
ErrorPriority priority
Definition: node.h:158
Location location
Definition: node.h:156
std::vector< std::string > context
Definition: node.h:157