Spicy
node.h
1 // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <cinttypes>
6 #include <iostream>
7 #include <list>
8 #include <map>
9 #include <memory>
10 #include <optional>
11 #include <set>
12 #include <string>
13 #include <utility>
14 #include <variant>
15 #include <vector>
16 
17 namespace hilti {
18 namespace trait {
19 class isNode {};
20 } // namespace trait
21 } // namespace hilti
22 
23 #include <hilti/ast/meta.h>
24 #include <hilti/ast/node_ref.h>
25 #include <hilti/ast/scope.h>
26 #include <hilti/base/type_erase.h>
27 #include <hilti/base/util.h>
28 
29 namespace hilti {
30 
31 class Node;
32 class NodeRef;
33 
34 namespace node {
35 namespace detail {
36 
38 using PropertyValue = std::variant<bool, const char*, double, int, int64_t, unsigned int, uint64_t, std::string>;
39 
41 inline std::string to_string(PropertyValue v) {
42  struct Visitor {
43  auto operator()(bool s) { return std::string(s ? "true" : "false"); }
44  auto operator()(const char* s) { return util::escapeUTF8(s); }
45  auto operator()(double d) { return util::fmt("%.6f", d); }
46  auto operator()(int i) { return util::fmt("%d", i); }
47  auto operator()(int64_t i) { return util::fmt("%" PRId64, i); }
48  auto operator()(std::string s) { return util::escapeUTF8(s); }
49  auto operator()(unsigned int u) { return util::fmt("%u", u); }
50  auto operator()(uint64_t u) { return util::fmt("%" PRIu64, u); }
51  };
52 
53  return std::visit(Visitor(), v);
54 };
55 
56 } // namespace detail
57 
59 enum class ErrorPriority {
60  Normal,
61  Low
62 };
63 
65 struct Error {
66  std::string message;
68  std::vector<std::string> context;
69  ErrorPriority priority = ErrorPriority::Normal;
71  // Comparision considers message & location, so that we can unique based
72  // on those two.
73  bool operator<(const Error& other) const {
74  return std::tie(message, location) < std::tie(other.message, other.location);
75  }
76 };
77 
83 using Properties = std::map<std::string, node::detail::PropertyValue>;
84 
85 namespace detail {
86 #include <hilti/autogen/__node.h>
87 
88 } // namespace detail
89 } // namespace node
90 
97 class Node final : public node::detail::Node {
98 public:
100  template<typename T, typename std::enable_if_t<std::is_base_of<trait::isNode, T>::value>* = nullptr>
101  Node(T t) : node::detail::Node(std::move(t)) {}
102 
103  Node(const Node& other) : node::detail::Node::Node(other), _scope(other._scope) {}
104 
105  Node(Node&& other) noexcept
106  : node::detail::Node::Node(std::move(other)),
107  _control_ptr(std::move(other._control_ptr)),
108  _scope(std::move(other._scope)),
109  _errors(std::move(other._errors)) {
110  if ( _control_ptr )
111  _control_ptr->_node = this;
112  }
113 
114  Node() = delete;
115 
116  explicit Node(IntrusivePtr<hilti::node::detail::Concept> data) : node::detail::Node(std::move(data)) {}
117 
118  ~Node() final {
119  if ( _control_ptr )
120  _control_ptr->_node = nullptr;
121  }
122 
130  uint64_t rid() const { return _control_ptr ? _control_ptr->_rid : 0; }
131 
137  std::string renderedRid() const { return rid() ? util::fmt("%%%" PRIu64, rid()) : "%???"; };
138 
146  if ( ! _scope )
147  _scope = make_intrusive<Scope>();
148 
149  return _scope;
150  }
151 
155  void setScope(IntrusivePtr<Scope> new_scope) { _scope = std::move(new_scope); }
156 
158  void clearScope() {
159  if ( _scope )
160  _scope->clear();
161  }
162 
164  std::vector<node::Error> errors() const {
165  if ( _errors )
166  return *_errors;
167  else
168  return {};
169  }
170 
172  bool hasErrors() const { return _errors && _errors->size(); }
173 
175  void clearErrors() {
176  if ( _errors )
177  _errors.reset();
178  }
179 
188  void addError(std::string msg, std::vector<std::string> context = {}) {
189  addError(std::move(msg), location(), std::move(context));
190  }
191 
200  void addError(std::string msg, Location l, std::vector<std::string> context = {}) {
201  addError(std::move(msg), location(), node::ErrorPriority::Normal, std::move(context));
202  }
203 
212  void addError(std::string msg, Location l, node::ErrorPriority priority, std::vector<std::string> context = {}) {
213  node::Error error;
214  error.message = std::move(msg);
215  error.location = std::move(l);
216  error.context = std::move(context);
217  error.priority = priority;
218 
219  if ( ! _errors )
220  _errors = std::make_unique<std::vector<node::Error>>();
221 
222  _errors->push_back(std::move(error));
223  }
224 
232  std::string render(bool include_location = true) const;
233 
243  void print(std::ostream& out, bool compact = false) const;
244 
246  const Location& location() const { return meta().location(); }
247 
249  template<typename T>
250  void assertIsA() {
251  if ( ! isA<T>() ) {
252  std::cerr << "Assertion failure: Node expected to be a " << typeid(T).name() << " but is a "
253  << typeid_().name() << std::endl;
255  }
256  }
257 
259  operator std::string() const {
260  std::stringstream buf;
261  print(buf, true);
262  return buf.str();
263  }
264 
269  Node& operator=(const Node& n) {
270  _scope = n._scope;
271  node::detail::ErasedBase::operator=(n);
272  return *this;
273  }
274 
279  Node& operator=(Node&& n) noexcept {
280  _scope = std::move(n._scope);
281  node::detail::ErasedBase::operator=(std::move(n));
282  return *this;
283  }
284 
290  template<typename T>
291  Node& operator=(const T& t) {
292  node::detail::ErasedBase::operator=(to_node(t));
293  return *this;
294  }
295 
296 private:
297  friend class NodeRef;
298 
299  // Returns (and potentially) created the control block for this node that
300  // NodeRef uses to maintain links to it.
302  if ( ! _control_ptr )
303  _control_ptr = make_intrusive<node_ref::detail::Control>(this);
304 
305  return _control_ptr;
306  }
307 
308  IntrusivePtr<node_ref::detail::Control> _control_ptr = nullptr;
309  mutable IntrusivePtr<Scope> _scope = nullptr;
310  std::unique_ptr<std::vector<node::Error>> _errors = nullptr;
311 };
312 
318 class NodeBase : public trait::isNode {
319 public:
325  NodeBase(Meta meta) : _meta(std::move(meta)) {}
326 
333  NodeBase(std::vector<Node> childs, Meta meta) : _meta(std::move(meta)) {
334  for ( auto& c : childs )
335  addChild(std::move(c));
336  }
337 
338  NodeBase() = default;
339 
347  template<typename T>
348  const T& child(int i) const {
349  return _childs[i].as<T>();
350  }
351 
358  template<typename T>
359  void assertChildIsA(int i) {
360  _childs[i].template assertIsA<T>();
361  }
362 
372  template<typename T>
373  std::vector<T> childs(int begin, int end) const {
374  std::vector<T> n;
375 
376  if ( end < 0 )
377  end = _childs.size();
378 
379  for ( auto i = begin; i < end; i++ )
380  n.emplace_back(_childs[i].as<T>());
381 
382  return n;
383  }
384 
391  template<typename T>
392  std::vector<T> childsOfType() const {
393  std::vector<T> n;
394  for ( auto& c : _childs ) {
395  if ( auto x = c.tryAs<T>() )
396  n.emplace_back(*x);
397  }
398 
399  return n;
400  }
401 
408  template<typename T>
409  auto nodesOfType() const {
410  std::vector<std::reference_wrapper<const Node>> n;
411  for ( const auto& c : _childs ) {
412  if ( c.isA<T>() )
413  n.emplace_back(c);
414  }
415 
416  return n;
417  }
418 
419  template<typename T>
420  auto nodesOfType() {
421  std::vector<std::reference_wrapper<Node>> n;
422  for ( auto& c : _childs ) {
423  if ( c.isA<T>() )
424  n.emplace_back(c);
425  }
426 
427  return n;
428  }
429 
434  void addChild(Node n) {
435  if ( _meta.location() && ! n.location() ) {
436  auto m = n.meta();
437  m.setLocation(_meta.location());
438  n.setMeta(std::move(m));
439  }
440 
441  _childs.push_back(std::move(n));
442  }
443 
445  auto& childs() const { return _childs; }
447  auto& childs() { return _childs; }
449  auto& meta() const { return _meta; }
451  void setMeta(Meta m) { _meta = std::move(m); }
453  const NodeRef& originalNode() const { return _orig; }
455  void setOriginalNode(const NodeRef& n) { _orig = n; }
457  void clearCache() {}
458 
459 private:
460  std::vector<::hilti::Node> _childs;
461  Meta _meta;
462  NodeRef _orig;
463 };
464 
465 namespace node {
466 
469 public:
471  auto properties() const { return node::Properties{}; }
472 
477  static None create() { return None(); }
478 
479 private:
480  None() : NodeBase(Meta()) {}
481 };
482 
484 extern const Node none;
485 
486 } // namespace node
487 
488 inline const Node& to_node(const node::None& /* n */) { return node::none; }
489 
494 template<typename T, IF_SAME(T, Node)> // Don't allow derived classes.
495 inline Node to_node(const T& n) {
496  return n;
497 }
498 
500 template<typename T>
501 Node to_node(std::optional<T> t) {
502  if ( t )
503  return to_node(std::move(*t));
504 
505  return to_node(node::none);
506 }
507 
512 template<typename T>
513 std::vector<Node> nodes(std::vector<T> t) {
514  std::vector<Node> v;
515  v.reserve(t.size());
516  for ( const auto& i : t )
517  v.emplace_back(std::move(i));
518  return v;
519 }
520 
525 template<typename T>
526 std::vector<Node> nodes(std::list<T> t) {
527  std::vector<Node> v;
528  for ( const auto& i : t )
529  v.emplace_back(std::move(i));
530  return v;
531 }
532 
537 template<typename T>
538 std::vector<Node> nodes(std::set<T> t) {
539  std::vector<Node> v;
540  v.reserve(t.size());
541  for ( const auto& i : t )
542  v.emplace_back(std::move(i));
543  return v;
544 }
545 
550 template<typename T, typename U>
551 std::vector<Node> nodes(std::vector<std::pair<T, U>> t) {
552  std::vector<Node> v;
553  v.reserve(t.size() * 2);
554  for ( const auto& i : t ) {
555  v.emplace_back(std::move(i.first));
556  v.emplace_back(std::move(i.second));
557  }
558  return v;
559 }
560 
562 template<typename T>
563 std::vector<Node> nodes(T t) {
564  return {to_node(std::move(t))};
565 }
566 
571 template<typename T, typename... Ts>
572 std::vector<Node> nodes(T t, Ts... ts) {
573  return util::concat(std::move(nodes(t)), nodes(std::move(ts)...));
574 }
575 
582 namespace node {
583 template<typename T, typename Other, IF_DERIVED_FROM(T, trait::isNode), IF_DERIVED_FROM(Other, trait::isNode)>
584 bool isEqual(const T* this_, const Other& other) {
585  if ( auto o = other.template tryAs<T>() )
586  return *this_ == *o;
587 
588  return false;
589 }
590 
591 } // namespace node
592 
594 inline std::ostream& operator<<(std::ostream& out, const Node& n) {
595  n.print(out, true);
596  return out;
597 }
598 
599 namespace node {
600 namespace detail {
601 // Backend to NodeBase::flattenedChilds.
602 template<typename T>
603 void flattenedChilds(const Node& n, std::vector<T>* dst) {
604  for ( const auto& c : n.childs() ) {
605  if ( auto t = c.tryAs<T>() )
606  dst->push_back(*t);
607 
608  flattenedChilds(c, dst);
609  }
610 }
611 
612 } // namespace detail
613 
618 template<typename T>
619 std::vector<T> flattenedChilds(const Node& n) {
620  std::vector<T> dst;
621  detail::flattenedChilds<T>(n, &dst);
622  return dst;
623 }
624 
625 } // namespace node
626 
627 } // namespace hilti
628 
629 extern hilti::node::Properties operator+(const hilti::node::Properties& p1, const hilti::node::Properties& p2);
Definition: global-optimizer.cc:45
ErrorPriority priority
Definition: node.h:69
std::vector< std::string > context
Definition: node.h:68
void setOriginalNode(const NodeRef &n)
Definition: node.h:455
uint64_t rid() const
Definition: node.h:130
IntrusivePtr< Scope > scope() const
Definition: node.h:145
NodeBase(Meta meta)
Definition: node.h:325
auto & childs() const
Definition: node.h:445
std::vector< T > childs(int begin, int end) const
Definition: node.h:373
void assertIsA()
Definition: node.h:250
Node & operator=(const T &t)
Definition: node.h:291
void addError(std::string msg, Location l, std::vector< std::string > context={})
Definition: node.h:200
const Node none
Definition: node.cc:12
void abort_with_backtrace()
Definition: util.cc:180
bool hasErrors() const
Definition: node.h:172
auto & childs()
Definition: node.h:447
Definition: optional.h:79
std::vector< T > childsOfType() const
Definition: node.h:392
static None create()
Definition: node.h:477
void addChild(Node n)
Definition: node.h:434
Definition: node.h:468
std::vector< T > concat(std::vector< T > v1, const std::vector< T > &v2)
Definition: util.h:571
void clearScope()
Definition: node.h:158
void addError(std::string msg, std::vector< std::string > context={})
Definition: node.h:188
void setScope(IntrusivePtr< Scope > new_scope)
Definition: node.h:155
const Location & location() const
Definition: node.h:246
Location location
Definition: node.h:67
Node & operator=(Node &&n) noexcept
Definition: node.h:279
Definition: meta.h:18
Definition: node.h:65
std::string renderedRid() const
Definition: node.h:137
void clearErrors()
Definition: node.h:175
std::string fmt(const char *fmt, const Args &... args)
Definition: util.h:80
void addError(std::string msg, Location l, node::ErrorPriority priority, std::vector< std::string > context={})
Definition: node.h:212
auto properties() const
Definition: node.h:471
std::vector< node::Error > errors() const
Definition: node.h:164
const NodeRef & originalNode() const
Definition: node.h:453
Definition: intrusive-ptr.h:69
void setMeta(Meta m)
Definition: node.h:451
void print(std::ostream &out, bool compact=false) const
Definition: node.cc:84
std::map< std::string, node::detail::PropertyValue > Properties
Definition: node.h:83
std::string message
Definition: node.h:66
const T & child(int i) const
Definition: node.h:348
auto nodesOfType() const
Definition: node.h:409
Definition: node.h:97
Definition: node_ref.h:44
Definition: location.h:17
Definition: node.h:19
Node & operator=(const Node &n)
Definition: node.h:269
ErrorPriority
Definition: node.h:59
auto & meta() const
Definition: node.h:449
Definition: location.h:86
Node(T t)
Definition: node.h:101
void assertChildIsA(int i)
Definition: node.h:359
NodeBase(std::vector< Node > childs, Meta meta)
Definition: node.h:333
Definition: node.h:318
void clearCache()
Definition: node.h:457