Spicy
switch.h
1 // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <string>
6 #include <utility>
7 #include <vector>
8 
9 #include <hilti/ast/declarations/local-variable.h>
10 #include <hilti/ast/expression.h>
11 #include <hilti/ast/expressions/id.h>
12 #include <hilti/ast/expressions/unresolved-operator.h>
13 #include <hilti/ast/statement.h>
14 
15 namespace hilti {
16 namespace statement {
17 
18 class Switch;
19 
20 namespace switch_ {
21 
22 using Default = struct {};
23 
34 class Case : public NodeBase {
35 public:
36  Case(hilti::Expression expr, Statement body, Meta m = Meta())
37  : NodeBase(nodes(std::move(body), std::move(expr)), std::move(m)), _end_exprs(2) {}
38  Case(std::vector<hilti::Expression> exprs, Statement body, Meta m = Meta())
39  : NodeBase(nodes(std::move(body), std::move(exprs)), std::move(m)), _end_exprs(childs().size()) {}
40  Case(Default /*unused*/, Statement body, Meta m = Meta())
41  : NodeBase(nodes(std::move(body)), std::move(m)), _end_exprs(1) {}
42  Case() = default;
43 
44  auto expressions() const { return childs<hilti::Expression>(1, _end_exprs); }
45  auto preprocessedExpressions() const { return childs<hilti::Expression>(_end_exprs, -1); }
46  const auto& body() const { return child<Statement>(0); }
47 
48  bool isDefault() const { return expressions().empty(); }
49 
51  auto& _bodyNode() { return childs()[0]; }
52 
54  auto properties() const { return node::Properties{}; }
55 
56  bool operator==(const Case& other) const { return expressions() == other.expressions() && body() == other.body(); }
57 
65  static Case setExpressions(Case c, std::vector<hilti::Expression> exprs) {
66  c.childs() = hilti::nodes(c._bodyNode(), std::move(exprs));
67  return c;
68  }
69 
70 private:
71  friend class hilti::statement::Switch;
72 
73  void _addPreprocessedExpression(hilti::Expression e) { childs().emplace_back(std::move(e)); }
74 
75  int _end_exprs{};
76 };
77 
78 inline Node to_node(Case c) { return Node(std::move(c)); }
79 
80 } // namespace switch_
81 
83 class Switch : public NodeBase, public hilti::trait::isStatement {
84 public:
85  Switch(hilti::Expression cond, const std::vector<switch_::Case>& cases, Meta m = Meta())
86  : NodeBase(nodes(node::none, std::move(cond), cases), std::move(m)) {
87  _preprocessCases("__x");
88  }
89 
90  Switch(const hilti::Declaration& init, hilti::Expression cond, const std::vector<switch_::Case>& cases,
91  Meta m = Meta())
92  : NodeBase(nodes(init, std::move(cond), cases), std::move(m)) {
93  if ( ! init.isA<declaration::LocalVariable>() )
94  logger().internalError("initialization for 'switch' must be a local declaration");
95  _preprocessCases(init.id());
96  }
97 
98  auto init() const { return childs()[0].tryReferenceAs<hilti::Declaration>(); }
99  const auto& expression() const { return childs()[1].as<hilti::Expression>(); }
100  auto type() const {
101  if ( auto i = init() )
102  return i->as<declaration::LocalVariable>().type();
103 
104  return expression().type();
105  }
106 
107  auto cases() const {
108  std::vector<switch_::Case> cases;
109  for ( auto& c : childs<switch_::Case>(2, -1) ) {
110  if ( ! c.isDefault() )
111  cases.push_back(c);
112  }
113 
114  return cases;
115  }
116 
117  auto caseNodes() { return nodesOfType<switch_::Case>(); }
118 
119  std::optional<switch_::Case> default_() const {
120  for ( auto& c : childs<switch_::Case>(2, -1) ) {
121  if ( c.isDefault() )
122  return c;
123  }
124  return {};
125  }
126 
127  bool operator==(const Switch& other) const {
128  return init() == other.init() && expression() == other.expression() && default_() == other.default_() &&
129  cases() == other.cases();
130  }
131 
133  auto& _lastCaseNode() { return childs().back(); }
134 
136  void _addCase(switch_::Case case_) {
137  for ( const auto& c : case_.expressions() )
138  case_._addPreprocessedExpression(expression::UnresolvedOperator(operator_::Kind::Equal,
139  {expression::UnresolvedID(ID(_id)), c},
140  c.meta()));
141 
142  addChild(std::move(case_));
143  }
144 
145 
147  auto isEqual(const Statement& other) const { return node::isEqual(this, other); }
148 
150  auto properties() const { return node::Properties{}; }
151 
152 private:
153  void _preprocessCases(const std::string& id) {
154  _id = id;
155 
156  for ( uint64_t i = 2; i < childs().size(); i++ ) {
157  auto& case_ = childs()[i].as<switch_::Case>();
158  for ( const auto& c : case_.expressions() )
159  case_._addPreprocessedExpression(expression::UnresolvedOperator(operator_::Kind::Equal,
160  {expression::UnresolvedID(ID(id)), c},
161  c.meta()));
162  }
163  }
164 
165  std::string _id;
166 };
167 
168 } // namespace statement
169 } // namespace hilti
static Case setExpressions(Case c, std::vector< hilti::Expression > exprs)
Definition: switch.h:65
Definition: local-variable.h:19
auto isEqual(const Statement &other) const
Definition: switch.h:147
auto & childs() const
Definition: node.h:445
std::vector< T > childs(int begin, int end) const
Definition: node.h:373
Definition: unresolved-operator.h:16
const Node none
Definition: node.cc:12
auto properties() const
Definition: switch.h:54
void addChild(Node n)
Definition: node.h:434
Definition: meta.h:18
Definition: statement.h:14
Definition: switch.h:83
std::map< std::string, node::detail::PropertyValue > Properties
Definition: node.h:83
Definition: node.h:97
void _addCase(switch_::Case case_)
Definition: switch.h:136
auto & _bodyNode()
Definition: switch.h:51
Definition: switch.h:34
auto properties() const
Definition: switch.h:150
Definition: node.h:318
auto & _lastCaseNode()
Definition: switch.h:133