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 <string>
13 #include <unordered_set>
14 #include <utility>
15 #include <vector>
16 
17 #include <hilti/ast/doc-string.h>
18 #include <hilti/ast/forward.h>
19 #include <hilti/ast/id.h>
20 #include <hilti/ast/meta.h>
21 #include <hilti/ast/node-range.h>
22 #include <hilti/ast/node-tag.h>
23 #include <hilti/ast/scope.h>
24 #include <hilti/ast/visitor-dispatcher.h>
25 
26 #define __HILTI_NODE_COMMON_final(NS, CLASS) \
27  ::hilti::Node* _clone(::hilti::ASTContext* ctx) const final { return ctx->make<CLASS>(*this); }
28 
29 #define __HILTI_NODE_COMMON_override(NS, CLASS)
30 
31 #define __HILTI_NODE_COMMON(NS, CLASS, override_) \
32  friend class ::NS::builder::NodeBuilder; \
33  friend class hilti::ASTContext; \
34  friend class hilti::Node; \
35  std::string _typename() const override { return hilti::util::typename_(*this); } \
36  __HILTI_NODE_COMMON_##override_(NS, CLASS)
37 
38 #define __HILTI_NODE_0(NS, CLASS, override_) \
39  __HILTI_NODE_COMMON(NS, CLASS, override_) \
40  \
41  static constexpr uint16_t NodeLevel = 1; \
42  static constexpr ::hilti::node::Tag NodeTag = ::hilti::node::tag::CLASS; \
43  static constexpr ::hilti::node::Tags NodeTags = {::hilti::node::tag::Node, ::hilti::node::tag::CLASS};
44 
45 #define HILTI_NODE_0(CLASS, override_) \
46  __HILTI_NODE_0(hilti, CLASS, override_) \
47  \
48  void dispatch(::hilti::visitor::Dispatcher& v) override_ { \
49  v(static_cast<::hilti::Node*>(this)); \
50  v(this); \
51  }
52 
53 #define __HILTI_NODE_1(NS, CLASS, BASE, override_) \
54  __HILTI_NODE_COMMON(NS, CLASS, override_) \
55  \
56  static constexpr uint16_t NodeLevel = 2; \
57  static constexpr ::hilti::node::Tag NodeTag = ::hilti::node::tag::CLASS; \
58  static constexpr ::hilti::node::Tags NodeTags = {::hilti::node::tag::Node, ::hilti::node::tag::BASE, \
59  ::hilti::node::tag::CLASS};
60 
61 #define HILTI_NODE_1(CLASS, BASE, override_) \
62  __HILTI_NODE_1(hilti, CLASS, BASE, override_) \
63  \
64  void dispatch(::hilti::visitor::Dispatcher& v) override_ { \
65  v(static_cast<::hilti::Node*>(this)); \
66  v(static_cast<BASE*>(this)); \
67  v(this); \
68  }
69 
70 #define __HILTI_NODE_2(NS, CLASS, BASE1, BASE2, override_) \
71  __HILTI_NODE_COMMON(NS, CLASS, override_) \
72  \
73  static constexpr uint16_t NodeLevel = 3; \
74  static constexpr ::hilti::node::Tag NodeTag = ::hilti::node::tag::CLASS; \
75  static constexpr ::hilti::node::Tags NodeTags = {::hilti::node::tag::Node, ::hilti::node::tag::BASE2, \
76  ::hilti::node::tag::BASE1, ::hilti::node::tag::CLASS};
77 
78 #define HILTI_NODE_2(CLASS, BASE1, BASE2, override_) \
79  __HILTI_NODE_2(hilti, CLASS, BASE1, BASE2, override_) \
80  \
81  void dispatch(::hilti::visitor::Dispatcher& v) override_ { \
82  v(static_cast<::hilti::Node*>(this)); \
83  v(static_cast<BASE1*>(this)); \
84  v(static_cast<BASE2*>(this)); \
85  v(this); \
86  }
87 
88 namespace hilti {
89 namespace builder {
90 class NodeBuilder;
91 }
92 
93 namespace node {
94 
95 namespace detail {
97 Node* deepcopy(ASTContext* ctx, Node* n, bool force);
98 } // namespace detail
99 
109 template<typename T>
110 T* deepcopy(ASTContext* ctx, T* n, bool force = false) {
111  if ( ! n )
112  return nullptr;
113 
114  return detail::deepcopy(ctx, n, force)->template as<T>();
115 }
116 
118 using PropertyValue = std::variant<bool, const char*, double, int, int64_t, unsigned int, uint64_t, std::string, ID,
119  std::optional<uint64_t>>;
120 
122 inline std::string to_string(const PropertyValue& v) {
123  struct Visitor {
124  auto operator()(bool s) { return std::string(s ? "true" : "false"); }
125  auto operator()(const char* s) { return util::escapeUTF8(s); }
126  auto operator()(double d) { return util::fmt("%.6f", d); }
127  auto operator()(int i) { return util::fmt("%d", i); }
128  auto operator()(int64_t i) { return util::fmt("%" PRId64, i); }
129  auto operator()(const std::string& s) { return util::escapeUTF8(s); }
130  auto operator()(const ID& id) { return id.str(); }
131  auto operator()(const std::optional<uint64_t>& u) { return u ? util::fmt("%" PRIu64, *u) : "<not set>"; }
132  auto operator()(unsigned int u) { return util::fmt("%u", u); }
133  auto operator()(uint64_t u) { return util::fmt("%" PRIu64, u); }
134  };
135 
136  return std::visit(Visitor(), v);
137 };
138 
140 enum class ErrorPriority {
141  High = 3,
142  Normal = 2,
143  Low = 1,
144  NoError = 0
145 };
146 
147 inline bool operator<(ErrorPriority x, ErrorPriority y) {
148  return static_cast<std::underlying_type_t<ErrorPriority>>(x) <
149  static_cast<std::underlying_type_t<ErrorPriority>>(y);
150 }
151 
153 struct Error {
154  std::string message;
156  std::vector<std::string> context;
157  ErrorPriority priority = ErrorPriority::Normal;
159  // Comparison considers message & location, so that we can unique based
160  // on those two.
161  bool operator<(const Error& other) const {
162  return std::tie(message, location) < std::tie(other.message, other.location);
163  }
164 };
165 
171 using Properties = std::map<std::string, node::PropertyValue>;
172 
174 template<typename T>
175 class RetainedPtr {
176 public:
177  RetainedPtr() = default;
178  RetainedPtr(T* n) : _node(n) { retain(); }
179  RetainedPtr(const RetainedPtr& other) : _node(other._node) { retain(); }
180  RetainedPtr(RetainedPtr&& other) noexcept : _node(other._node) {
181  other._node = nullptr;
182  // reuse the other's retain
183  }
184 
185  ~RetainedPtr() { release(); }
186 
187  RetainedPtr& operator=(const RetainedPtr& other) {
188  if ( this == &other )
189  return *this;
190 
191  release();
192  _node = other._node;
193  retain();
194  return *this;
195  }
196 
197  RetainedPtr& operator=(RetainedPtr&& other) noexcept {
198  if ( this == &other )
199  return *this;
200 
201  release();
202  _node = other._node;
203  other._node = nullptr;
204  // reuse the other's retain
205  return *this;
206  }
207 
208  void reset() {
209  release();
210  _node = nullptr;
211  }
212 
213  T* operator->() const { return _node; }
214  T& operator*() const { return *_node; }
215  explicit operator bool() const { return _node != nullptr; }
216  operator T*() const { return _node; }
217 
218  T* get() const { return _node; }
219 
220 private:
221  void retain() {
222  if ( _node )
223  _node->retain();
224  }
225 
226  void release() {
227  if ( _node ) {
228  _node->release();
229  _node = nullptr;
230  }
231  }
232 
233  T* _node = nullptr;
234 };
235 
236 } // namespace node
237 
239 class Node {
240 public:
241  virtual ~Node();
242 
244  node::Tag nodeTag() const {
245  // Get the last non-zero tag. The last element(s) may be unset
246  for ( auto it = _node_tags.rbegin(); it != _node_tags.rend(); it++ ) {
247  if ( *it != 0 )
248  return *it;
249  }
250 
251  return 0;
252  }
253 
255  bool hasParent() const { return _parent; }
256 
263  Node* parent(int i = 1) const {
264  if ( i == 0 )
265  return nullptr;
266 
267  Node* p = _parent;
268  for ( ; p && i > 1; i-- )
269  p = p->_parent;
270 
271  return p;
272  }
273 
280  template<typename T>
281  T* parent() const {
282  if ( ! _parent )
283  return nullptr;
284  else if ( _parent->isA_<T>() )
285  return static_cast<T*>(_parent);
286  else
287  return _parent->parent<T>();
288  }
289 
295  auto pathLength() const {
296  size_t i = 0;
297  for ( auto n = parent(); n; i++, n = n->parent() )
298  ;
299 
300  return i;
301  }
302 
303 
305  const auto& meta() const { return *_meta; }
306 
308  const auto& location() const { return _meta->location(); }
309 
311  void setMeta(Meta m) { _meta = Meta::get(std::move(m)); }
312 
317  auto scope() const { return _scope.get(); }
318 
325  if ( ! _scope )
326  _scope = std::make_unique<Scope>();
327 
328  return _scope.get();
329  }
330 
335  void clearScope() { _scope.reset(); }
336 
345  Result<std::pair<Declaration*, ID>> lookupID(const ID& id, const std::string_view& what) const;
346 
351  virtual bool inheritScope() const { return true; }
352 
357  std::string typename_() const { return _typename(); }
358 
360  uintptr_t identity() const { return reinterpret_cast<uintptr_t>(this); }
361 
363  const auto& children() const { return _children; }
364 
372  template<typename T>
373  T* child(unsigned int i) const {
374  if ( i >= _children.size() )
375  return nullptr;
376 
377  return _children[i] ? _children[i]->as<T>() : nullptr;
378  }
379 
387  template<typename T>
388  T* childTryAs(unsigned int i) const {
389  if ( i >= _children.size() )
390  return nullptr;
391 
392  return _children[i] ? _children[i]->tryAs<T>() : nullptr;
393  }
394 
402  Node* child(unsigned int i) const {
403  if ( i >= _children.size() )
404  return nullptr;
405 
406  return _children[i];
407  }
408 
419  template<typename T>
420  auto children(int begin, std::optional<int> end) const {
421  end = _normalizeEndIndex(begin, end);
422  if ( end )
423  return hilti::node::Range<T>(_children.begin() + begin, _children.begin() + *end);
424  else
425  return hilti::node::Range<T>();
426  }
427 
438  template<typename T>
439  auto children(int begin, std::optional<int> end) {
440  end = _normalizeEndIndex(begin, end);
441  if ( end )
442  return hilti::node::Range<T>(_children.begin() + begin, _children.begin() + *end);
443  else
444  return hilti::node::Range<T>();
445  }
446 
453  template<typename T>
454  node::Set<T> childrenOfType() const {
455  typename hilti::node::Set<T> n;
456  for ( auto c = _children.begin(); c != _children.end(); c = std::next(c) ) {
457  if ( ! *c )
458  continue;
459 
460  if ( auto t = (*c)->tryAs<T>() )
461  n.push_back(t);
462  }
463 
464  return n;
465  }
466 
474  Node* sibling(Node* n) const {
475  auto i = std::find(_children.begin(), _children.end(), n);
476  if ( i == _children.end() )
477  return nullptr;
478 
479  while ( true ) {
480  if ( ++i == _children.end() )
481  return nullptr;
482 
483  if ( *i )
484  return *i;
485  }
486  }
487 
497  void addChild(ASTContext* ctx, Node* n) {
498  if ( ! n ) {
499  _children.emplace_back(nullptr);
500  return;
501  }
502 
503  n = _newChild(ctx, n);
504 
505  if ( ! n->location() && _meta->location() )
506  n->_meta = _meta;
507 
508  _children.emplace_back(n);
509  n->_parent = this;
510  n->retain();
511  }
512 
520  void addChildren(ASTContext* ctx, const Nodes& children) {
521  for ( auto&& n : children )
522  addChild(ctx, n);
523  }
524 
531  void removeChild(Node* n) {
532  if ( ! n )
533  return;
534 
535  if ( auto i = std::find(_children.begin(), _children.end(), n); i != _children.end() ) {
536  (*i)->_parent = nullptr;
537  (*i)->release();
538  _children.erase(i);
539  }
540  }
541 
550  void removeChildren(int begin, std::optional<int> end) {
551  end = _normalizeEndIndex(begin, end);
552  if ( ! end )
553  return;
554 
555  auto end_ = _children.begin() + *end;
556  for ( auto i = _children.begin() + begin; i < end_; i++ ) {
557  if ( *i ) {
558  (*i)->_parent = nullptr;
559  (*i)->release();
560  }
561  }
562 
563  _children.erase(_children.begin() + begin, end_);
564  }
565 
577  void setChild(ASTContext* ctx, size_t idx, Node* n) {
578  if ( auto old = _children[idx] ) {
579  old->_parent = nullptr;
580  old->release();
581  }
582 
583  if ( ! n ) {
584  _children[idx] = nullptr;
585  return;
586  }
587 
588  n = _newChild(ctx, n);
589  n->_parent = this;
590  n->retain();
591 
592  if ( ! n->location() && _meta->location() )
593  n->_meta = _meta;
594 
595  _children[idx] = n;
596  }
597 
607  void replaceChildren(ASTContext* ctx, const Nodes& children);
608 
619  void replaceChild(ASTContext* ctx, Node* old, Node* new_);
620 
622  template<typename T>
623  bool isA() const {
624 #ifndef NDEBUG
625  _checkCast<T>(false);
626 #endif
627  return (T::NodeLevel < _node_tags.size() && T::NodeTag == _node_tags[T::NodeLevel]);
628  }
629 
636  template<typename T>
637  bool isA_() const {
638  return (T::NodeLevel < _node_tags.size() && T::NodeTag == _node_tags[T::NodeLevel]);
639  }
640 
645  template<typename T>
646  T* as() const {
647 #ifndef NDEBUG
648  _checkCast<T>(true);
649 #endif
650  return static_cast<const T*>(this);
651  }
652 
659  template<typename T>
660  T* as() {
661 #ifndef NDEBUG
662  _checkCast<T>(true);
663 #endif
664  return static_cast<T*>(this);
665  }
666 
671  template<typename T>
672  const T* tryAs() const {
673 #ifndef NDEBUG
674  _checkCast<T>(false);
675 #endif
676 
677  if ( isA<T>() )
678  return static_cast<const T*>(this);
679  else
680  return nullptr;
681  }
682 
687  template<typename T>
688  T* tryAs() {
689 #ifndef NDEBUG
690  _checkCast<T>(false);
691 #endif
692  if ( isA<T>() )
693  return static_cast<T*>(this);
694  else
695  return nullptr;
696  }
697 
705  template<typename T>
706  T* tryAs_() {
707  if ( isA_<T>() )
708  return static_cast<T*>(this);
709  else
710  return nullptr;
711  }
712 
725  void print(std::ostream& out, bool compact, bool user_visible) const;
726 
734  std::string print() const;
735 
743  std::string printRaw() const;
744 
749  operator std::string() const { return print(); }
750 
755  std::string dump() const;
756 
764  std::string renderSelf(bool include_location = true) const;
765 
774  void addError(std::string msg, std::vector<std::string> context = {}) {
775  addError(std::move(msg), location(), std::move(context));
776  }
777 
787  void addError(std::string msg, node::ErrorPriority priority, std::vector<std::string> context = {}) {
788  addError(std::move(msg), location(), priority, std::move(context));
789  }
790 
799  void addError(std::string msg, const Location& l, std::vector<std::string> context = {}) {
800  addError(std::move(msg), location(), node::ErrorPriority::Normal, std::move(context));
801  }
802 
811  void addError(std::string msg, Location l, node::ErrorPriority priority, std::vector<std::string> context = {}) {
812  node::Error error;
813  error.message = std::move(msg);
814  error.location = std::move(l);
815  error.context = std::move(context);
816  error.priority = priority;
817 
818  if ( ! _errors )
819  _errors = std::make_unique<std::vector<node::Error>>();
820 
821  _errors->emplace_back(std::move(error));
822  }
823 
825  bool hasErrors() const { return _errors && ! _errors->empty(); }
826 
828  const auto& errors() const {
829  static std::vector<node::Error> no_errors;
830  return _errors ? *_errors : no_errors;
831  }
832 
834  void clearErrors() { _errors.reset(); }
835 
840  void clearChildren();
841 
843  void retain() {
844  assert(_ref_count != -1); // dtor sets ref count to -1
845  ++_ref_count;
846  }
847 
852  void release() {
853  assert(_ref_count != -1); // dtor sets ref count to -1
854  assert(_ref_count > 0);
855  --_ref_count;
856  }
857 
859  bool isRetained() const { return _ref_count > 0; }
860 
866  virtual node::Properties properties() const { return {}; }
867 
869  virtual void dispatch(visitor::Dispatcher& v) = 0;
870 
878  virtual std::string_view branchTag() const { return ""; }
879 
880  Node& operator=(const Node& other) = delete;
881  Node& operator=(Node&& other) noexcept = delete;
882 
883  static constexpr uint16_t NodeLevel = 0; // distance from `Node` in the inheritance hierarchy
884  static constexpr ::hilti::node::Tag NodeTag = ::hilti::node::tag::Node; // this class' node tag
885  static constexpr ::hilti::node::Tags NodeTags = {::hilti::node::tag::Node}; // this class' inheritance path
886 
887 protected:
897  Node(ASTContext* ctx, node::Tags node_tags, Nodes children, Meta meta)
898  : _node_tags(node_tags), _meta(Meta::get(std::move(meta))) {
899  assert(! _node_tags.empty());
900  _children.reserve(children.size());
901  for ( auto&& c : children ) {
902  if ( c ) {
903  c = _newChild(ctx, c);
904  assert(! c->_parent);
905  c->_parent = this;
906  c->retain();
907  }
908 
909  _children.push_back(c);
910  }
911  }
912 
914  Node(ASTContext* ctx, node::Tags node_tags, Meta meta) : _node_tags(node_tags), _meta(Meta::get(std::move(meta))) {
915  assert(! _node_tags.empty());
916  }
917 
918  Node(Node&& other) = default;
919 
927  Node(const Node& other) : _node_tags(other._node_tags) {
928  _meta = other._meta;
929  _parent = nullptr;
930 
931  // Don't copy children. We can't copy the pointers because that would
932  // produce messed up parent pointers. And we can't deep copy here
933  // because we don't have the context. That's all ok because this isn't
934  // public anyways; to run this one needs to go through Node's cloning
935  // functions.
936  }
937 
943  virtual std::string _typename() const { return util::typename_(*this); }
944 
951  virtual Node* _clone(ASTContext* ctx) const = 0; // shallow copy
952 
957  virtual std::string _dump() const { return ""; }
958 
959 private:
960  friend Node* node::detail::deepcopy(ASTContext* ctx, Node* n, bool force);
961 
962  // Prepares a node for being added as a child, deep-copying it if it
963  // already has a parent.
964  static Node* _newChild(ASTContext* ctx, Node* child);
965 
966  // Do Python-style array indexing with negative indices.
967  std::optional<int> _normalizeEndIndex(int begin, std::optional<int> end) const {
968  if ( end && *end < 0 )
969  end = static_cast<int>(_children.size()) + *end;
970 
971  if ( ! end )
972  end = _children.size();
973 
974  if ( end > begin )
975  return end;
976  else {
977  // Some versions of GCC diagnose a maybe uninitialized variable
978  // here. Since we just return an unset optional, this should not be
979  // possible. (pragmas copied from intrusive-ptr.h, where there's a
980  // similar issue.)
981 #pragma GCC diagnostic push
982 #pragma GCC diagnostic ignored "-Wpragmas"
983 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
984 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
985  return {};
986 #pragma GCC diagnostic pop
987  }
988  }
989 
990 #ifndef NDEBUG
991  // Checks casts for common mistakes.
992  //
993  // @param enforce_success if true, we'll abort if the cast fails due to a type mismatch
994  template<typename T>
995  void _checkCast(bool enforce_success) const {
996  // Ensure that our RTTI information yields the same `isA<>` result as
997  // the C++ type system.
998  auto ours = (T::NodeLevel < _node_tags.size() && T::NodeTag == _node_tags[T::NodeLevel]);
999  auto theirs = (dynamic_cast<const T*>(this) != nullptr);
1000 
1001  if ( ours != theirs ) {
1002  std::cerr << util::fmt("internal error: Node::_checkCast() RTTI mismatch\n")
1003  << util::fmt("isA<T=%s>(%s) -> %s but dynamic_cast() says %s\n", typeid(T).name(), _typename(),
1004  ours ? "true" : "false", theirs ? "true" : "false")
1005  << util::fmt("T::type_level=%" PRIu16 " T::node_tags={%s} this->types={%s}\n", T::NodeLevel,
1006  node::to_string(T::NodeTags), node::to_string(_node_tags));
1007  abort();
1008  }
1009 
1010  if ( enforce_success && ! ours ) {
1011  std::cerr << util::fmt("internal error: unexpected type, want %s but have %s\n", util::typename_<T>(),
1012  _typename());
1013  abort();
1014  }
1015 
1016  // Debugging helper: If `this` is a `QualifiedType`, it's virtually
1017  // always wrong to try casting it into a class derived from
1018  // `UnqualifiedType`. Most likely that's meant to instead cast
1019  // `this->type()`. We abort here to make debugging such problems
1020  // easier.
1021  if ( std::is_base_of_v<UnqualifiedType, T> && ! std::is_same_v<UnqualifiedType, T> )
1022  _checkCastBackend();
1023  }
1024 #endif
1025 
1026  // Helper for _checkCast() to spot unexpected casts from `QualifiedType`.
1027  void _checkCastBackend() const;
1028 
1029  const node::Tags _node_tags; // inheritance path for the node
1030  int64_t _ref_count = 0; // number of pins currently held on the node; -1 is a special value set by the dtor to mark
1031  // an already destroyed node (for debugging)
1032  Node* _parent = nullptr; // parent node inside the AST, or null if not yet added to an AST
1033  Nodes _children; // set of child nodes
1034  const Meta* _meta; // meta information associated with the node; returned and managed by Meta::get()
1035 
1036  std::unique_ptr<Scope> _scope = nullptr; // scope associated with the node, or null if non (i.e., scope is empty)
1037  std::unique_ptr<std::vector<node::Error>> _errors; // errors associated with the node, or null if none
1038 };
1039 
1040 namespace node {
1041 
1047 public:
1052  ID uniqueID() const { return _id; }
1053 
1055  node::Properties properties() const { return {{"unique_id", _id}}; }
1056 
1057  WithUniqueID() = delete;
1058  WithUniqueID(const WithUniqueID& other) = default;
1059  WithUniqueID(WithUniqueID&& other) noexcept = default;
1060  ~WithUniqueID() = default;
1061 
1062  WithUniqueID& operator=(const WithUniqueID& other) = default;
1063  WithUniqueID& operator=(WithUniqueID&& other) noexcept = default;
1064 
1065 protected:
1073  WithUniqueID(const char* prefix) : _id(util::fmt("%s_%" PRIu64, prefix, _id_counter++)) {}
1074 
1075 private:
1076  ID _id;
1077 
1078  inline static uint64_t _id_counter = 0;
1079 };
1080 
1083 public:
1085  const std::optional<DocString>& documentation() const { return _doc; }
1086 
1088  void clearDocumentation() { _doc.reset(); }
1089 
1092  if ( doc )
1093  _doc = std::move(doc);
1094  else
1095  _doc.reset();
1096  }
1097 
1098 private:
1099  std::optional<DocString> _doc;
1100 };
1101 
1104 public:
1105  void recordSeen(const Node* n) { _seen.insert(n); }
1106  bool haveSeen(const Node* n) const { return _seen.find(n) != _seen.end(); }
1107  void clear() { _seen.clear(); }
1108 
1109 private:
1110  std::unordered_set<const Node*> _seen;
1111 };
1112 
1117 template<typename T>
1118 Nodes flatten(std::vector<T> t) {
1119  Nodes v;
1120  v.reserve(t.size());
1121  for ( auto it = std::make_move_iterator(t.begin()); it != std::make_move_iterator(t.end()); ++it )
1122  v.emplace_back(*it);
1123 
1124  return v;
1125 }
1126 
1131 template<typename T>
1132 Nodes flatten(hilti::node::Range<T> t) {
1133  Nodes v;
1134  v.reserve(t.size());
1135  for ( const auto& i : t )
1136  v.emplace_back(std::move(i));
1137 
1138  return v;
1139 }
1140 
1142 template<typename T = Node*>
1143 inline Nodes flatten(Node* n) {
1144  return {n};
1145 }
1146 
1148 template<typename T>
1149 inline Nodes flatten(T* n) {
1150  return {std::move(n)};
1151 }
1152 
1154 template<typename T = std::nullptr_t>
1155 inline Nodes flatten(std::nullptr_t) {
1156  return {nullptr};
1157 }
1158 
1160 inline Nodes flatten() { return Nodes(); }
1161 
1166 template<typename T, typename... Ts, std::enable_if_t<(0 != sizeof...(Ts))>* = nullptr>
1167 Nodes flatten(T t, Ts... ts) {
1168  return util::concat(std::move(flatten(std::move(t))), flatten(std::move(ts)...));
1169 }
1170 
1175 template<typename X, typename F>
1176 auto filter(const hilti::node::Range<X>& x, F f) {
1177  hilti::node::Set<X> y;
1178  for ( const auto& i : x ) {
1179  if ( f(i) )
1180  y.push_back(i);
1181  }
1182 
1183  return y;
1184 }
1185 
1190 template<typename X, typename F>
1191 auto filter(const hilti::node::Set<X>& x, F f) {
1192  hilti::node::Set<X> y;
1193  for ( const auto& i : x ) {
1194  if ( f(i) )
1195  y.push_back(i);
1196  }
1197 
1198  return y;
1199 }
1200 
1205 template<typename X, typename F>
1206 auto transform(const hilti::node::Range<X>& x, F f) {
1207  using Y = std::invoke_result_t<F, X*>;
1208  std::vector<Y> y;
1209  y.reserve(x.size());
1210  for ( const auto& i : x )
1211  y.push_back(f(i));
1212 
1213  return y;
1214 }
1215 
1220 template<typename X, typename F>
1221 auto transform(const hilti::node::Set<X>& x, F f) {
1222  using Y = std::invoke_result_t<F, X*>;
1223  std::vector<Y> y;
1224  y.reserve(x.size());
1225  for ( const auto& i : x )
1226  y.push_back(f(i));
1227 
1228  return y;
1229 }
1230 
1231 } // namespace node
1232 
1234 inline std::ostream& operator<<(std::ostream& out, const Node& n) {
1235  n.print(out, true, true);
1236  return out;
1237 }
1238 
1239 } // namespace hilti
1240 
1241 inline hilti::node::Properties operator+(hilti::node::Properties p1, hilti::node::Properties p2) {
1242  p1.merge(std::move(p2));
1243  return p1;
1244 }
Definition: ast-context.h:121
Definition: doc-string.h:13
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:239
Node * sibling(Node *n) const
Definition: node.h:474
T * parent() const
Definition: node.h:281
std::string renderSelf(bool include_location=true) const
Definition: node.cc:38
void clearScope()
Definition: node.h:335
T * tryAs_()
Definition: node.h:706
const T * tryAs() const
Definition: node.h:672
const auto & errors() const
Definition: node.h:828
void addError(std::string msg, std::vector< std::string > context={})
Definition: node.h:774
void setMeta(Meta m)
Definition: node.h:311
T * tryAs()
Definition: node.h:688
auto pathLength() const
Definition: node.h:295
T * as() const
Definition: node.h:646
bool hasErrors() const
Definition: node.h:825
bool isA_() const
Definition: node.h:637
auto children(int begin, std::optional< int > end) const
Definition: node.h:420
std::string print() const
Definition: node.cc:98
virtual Node * _clone(ASTContext *ctx) const =0
T * as()
Definition: node.h:660
Node * parent(int i=1) const
Definition: node.h:263
node::Tag nodeTag() const
Definition: node.h:244
void setChild(ASTContext *ctx, size_t idx, Node *n)
Definition: node.h:577
Node(const Node &other)
Definition: node.h:927
void clearErrors()
Definition: node.h:834
void addChild(ASTContext *ctx, Node *n)
Definition: node.h:497
void removeChild(Node *n)
Definition: node.h:531
virtual std::string _typename() const
Definition: node.h:943
T * childTryAs(unsigned int i) const
Definition: node.h:388
void addError(std::string msg, Location l, node::ErrorPriority priority, std::vector< std::string > context={})
Definition: node.h:811
virtual bool inheritScope() const
Definition: node.h:351
auto children(int begin, std::optional< int > end)
Definition: node.h:439
uintptr_t identity() const
Definition: node.h:360
const auto & location() const
Definition: node.h:308
void release()
Definition: node.h:852
auto scope() const
Definition: node.h:317
const auto & children() const
Definition: node.h:363
auto getOrCreateScope()
Definition: node.h:324
virtual std::string_view branchTag() const
Definition: node.h:878
void replaceChild(ASTContext *ctx, Node *old, Node *new_)
Definition: node.cc:110
Node * child(unsigned int i) const
Definition: node.h:402
void addError(std::string msg, node::ErrorPriority priority, std::vector< std::string > context={})
Definition: node.h:787
void replaceChildren(ASTContext *ctx, const Nodes &children)
Definition: node.cc:121
const auto & meta() const
Definition: node.h:305
virtual std::string _dump() const
Definition: node.h:957
Node(ASTContext *ctx, node::Tags node_tags, Nodes children, Meta meta)
Definition: node.h:897
void removeChildren(int begin, std::optional< int > end)
Definition: node.h:550
bool isRetained() const
Definition: node.h:859
T * child(unsigned int i) const
Definition: node.h:373
node::Set< T > childrenOfType() const
Definition: node.h:454
std::string printRaw() const
Definition: node.cc:104
std::string dump() const
Definition: node.cc:31
Node(ASTContext *ctx, node::Tags node_tags, Meta meta)
Definition: node.h:914
void clearChildren()
Definition: node.cc:128
bool hasParent() const
Definition: node.h:255
virtual node::Properties properties() const
Definition: node.h:866
virtual void dispatch(visitor::Dispatcher &v)=0
void addChildren(ASTContext *ctx, const Nodes &children)
Definition: node.h:520
bool isA() const
Definition: node.h:623
void retain()
Definition: node.h:843
Result< std::pair< Declaration *, ID > > lookupID(const ID &id, const std::string_view &what) const
Definition: node.cc:214
std::string typename_() const
Definition: node.h:357
void addError(std::string msg, const Location &l, std::vector< std::string > context={})
Definition: node.h:799
Definition: forward.h:758
Definition: node.h:1103
Definition: node-range.h:92
Definition: node.h:175
Definition: node.h:1082
void clearDocumentation()
Definition: node.h:1088
const std::optional< DocString > & documentation() const
Definition: node.h:1085
void setDocumentation(DocString doc)
Definition: node.h:1091
Definition: node.h:1046
ID uniqueID() const
Definition: node.h:1052
node::Properties properties() const
Definition: node.h:1055
WithUniqueID(const char *prefix)
Definition: node.h:1073
Definition: result.h:71
Definition: visitor-dispatcher.h:9
auto transform(const C &x, F f)
Definition: util.h:417
Definition: node.h:153
std::string message
Definition: node.h:154
ErrorPriority priority
Definition: node.h:157
Location location
Definition: node.h:155
std::vector< std::string > context
Definition: node.h:156