Spicy
operator.h
1 // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <functional>
6 #include <string>
7 #include <utility>
8 #include <variant>
9 #include <vector>
10 
11 #include <hilti/ast/expression.h>
12 #include <hilti/ast/operator.h>
13 #include <hilti/ast/types/doc-only.h>
14 #include <hilti/ast/types/type.h>
15 #include <hilti/base/logger.h>
16 #include <hilti/base/type_erase.h>
17 #include <hilti/base/util.h>
18 #include <hilti/base/visitor-types.h>
19 
20 namespace hilti {
21 
22 namespace visitor {}
23 
24 namespace expression {
25 namespace resolved_operator::detail {
26 class ResolvedOperator;
27 } // namespace resolved_operator::detail
28 
29 using ResolvedOperator = resolved_operator::detail::ResolvedOperator;
30 
31 } // namespace expression
32 
33 namespace trait {
35 class isOperator : public isNode {};
36 } // namespace trait
37 
38 namespace expression {
39 class UnresolvedOperator;
40 } // namespace expression
41 
42 namespace operator_ {
43 
44 using position_t = visitor::Position<Node&>;
45 using const_position_t = visitor::Position<const Node&>;
46 
47 using OperandType = std::variant<Type, std::function<std::optional<Type>(const hilti::node::Range<Expression>&,
48  const hilti::node::Range<Expression>&)>>;
49 
50 inline std::optional<Type> type(const OperandType& t, const hilti::node::Range<Expression>& orig_ops,
51  const hilti::node::Range<Expression>& resolved_ops) {
52  if ( const auto& f = std::get_if<std::function<std::optional<Type>(const hilti::node::Range<Expression>&,
53  const hilti::node::Range<Expression>&)>>(&t) )
54  return (*f)(orig_ops, resolved_ops);
55 
56  return std::get<Type>(t);
57 }
58 
59 inline std::optional<Type> type(const OperandType& t, const hilti::node::Range<Expression>& orig_ops,
60  const std::vector<Expression>& resolved_ops) {
61  auto resolved_ops_as_nodes = hilti::nodes(resolved_ops);
62  return type(t, orig_ops, hilti::node::Range<Expression>(resolved_ops_as_nodes));
63 }
64 
65 inline std::optional<Type> type(const OperandType& t, const std::vector<Expression>& orig_ops,
66  const std::vector<Expression>& resolved_ops) {
67  auto orig_ops_as_nodes = hilti::nodes(orig_ops);
68  return type(t, hilti::node::Range<Expression>(orig_ops_as_nodes), resolved_ops);
69 }
70 
71 inline auto operandType(unsigned int op, const char* doc = "<no-doc>") {
72  return [=](const hilti::node::Range<Expression>& /* orig_ops */,
73  const hilti::node::Range<Expression>& resolved_ops) -> std::optional<Type> {
74  if ( resolved_ops.empty() )
75  return type::DocOnly(doc);
76 
77  if ( op >= resolved_ops.size() )
78  logger().internalError(util::fmt("operandType(): index %d out of range, only %" PRIu64 " ops available", op,
79  resolved_ops.size()));
80 
81  return resolved_ops[op].type();
82  };
83 }
84 
85 inline auto elementType(unsigned int op, const char* doc = "<type of element>", bool infer_const = true) {
86  return [=](const hilti::node::Range<Expression>& /* orig_ops */,
87  const hilti::node::Range<Expression>& resolved_ops) -> std::optional<Type> {
88  if ( resolved_ops.empty() )
89  return type::DocOnly(doc);
90 
91  if ( op >= resolved_ops.size() )
92  logger().internalError(util::fmt("elementType(): index %d out of range, only %" PRIu64 " ops available", op,
93  resolved_ops.size()));
94 
95  if ( type::isIterable(resolved_ops[op].type()) ) {
96  auto t = resolved_ops[op].type().elementType();
97  return (infer_const && resolved_ops[op].isConstant()) ? type::constant(t) : std::move(t);
98  }
99 
100  return {};
101  };
102 }
103 
104 inline auto constantElementType(unsigned int op, const char* doc = "<type of element>") {
105  return [=](const hilti::node::Range<Expression>& /* orig_ops */,
106  const hilti::node::Range<Expression>& resolved_ops) -> std::optional<Type> {
107  if ( resolved_ops.empty() )
108  return type::DocOnly(doc);
109 
110  if ( op >= resolved_ops.size() )
111  logger().internalError(util::fmt("elementType(): index %d out of range, only %" PRIu64 " ops available", op,
112  resolved_ops.size()));
113 
114  if ( type::isIterable(resolved_ops[op].type()) )
115  return type::constant(resolved_ops[op].type().elementType());
116 
117  return {};
118  };
119 }
120 
121 inline auto iteratorType(unsigned int op, bool const_, const char* doc = "<iterator>") {
122  return [=](const hilti::node::Range<Expression>& /* orig_ops */,
123  const hilti::node::Range<Expression>& resolved_ops) -> std::optional<Type> {
124  if ( resolved_ops.empty() )
125  return type::DocOnly(doc);
126 
127  if ( op >= resolved_ops.size() )
128  logger().internalError(util::fmt("iteratorType(): index %d out of range, only %" PRIu64 " ops available",
129  op, resolved_ops.size()));
130 
131  if ( type::isIterable(resolved_ops[op].type()) )
132  return resolved_ops[op].type().iteratorType(const_);
133 
134  return {};
135  };
136 }
137 
138 inline auto dereferencedType(unsigned int op, const char* doc = "<dereferenced type>", bool infer_const = true) {
139  return [=](const hilti::node::Range<Expression>& /* orig_ops */,
140  const hilti::node::Range<Expression>& resolved_ops) -> std::optional<Type> {
141  if ( resolved_ops.empty() )
142  return type::DocOnly(doc);
143 
144  if ( op >= resolved_ops.size() )
145  logger().internalError(util::fmt("dereferencedType(): index %d out of range, only %" PRIu64
146  " ops available",
147  op, resolved_ops.size()));
148 
149  if ( type::isDereferenceable(resolved_ops[op].type()) ) {
150  auto t = resolved_ops[op].type().dereferencedType();
151 
152  if ( ! infer_const )
153  return std::move(t);
154 
155  return resolved_ops[op].isConstant() ? type::constant(t) : type::nonConstant(t);
156  }
157 
158  return {};
159  };
160 }
161 
162 inline auto sameTypeAs(unsigned int op, const char* doc = "<no-doc>") {
163  return [=](const hilti::node::Range<Expression>& /* orig_ops */,
164  const hilti::node::Range<Expression>& resolved_ops) -> std::optional<Type> {
165  if ( resolved_ops.empty() )
166  return type::DocOnly(doc);
167 
168  if ( op >= resolved_ops.size() )
169  logger().internalError(util::fmt("sameTypeAs(): index %d out of range, only %" PRIu64 " ops available", op,
170  resolved_ops.size()));
171 
172  return resolved_ops[op].type();
173  };
174 }
175 
176 inline auto typedType(unsigned int op, const char* doc = "<type>") {
177  return [=](const hilti::node::Range<Expression>& /* orig_ops */,
178  const hilti::node::Range<Expression>& resolved_ops) -> std::optional<Type> {
179  if ( resolved_ops.empty() )
180  return type::DocOnly(doc);
181 
182  if ( op >= resolved_ops.size() )
183  logger().internalError(util::fmt("typedType(): index %d out of range, only %" PRIu64 " ops available", op,
184  resolved_ops.size()));
185 
186  return resolved_ops[op].type().as<type::Type_>().typeValue();
187  };
188 }
189 
191 struct Operand {
192  Operand(Operand&&) = default;
193  Operand(const Operand&) = default;
194 
195  Operand& operator=(Operand&&) = default;
196  Operand& operator=(const Operand&) = default;
197 
198  Operand(std::optional<ID> _id = {}, OperandType _type = {}, bool _optional = false,
199  std::optional<Expression> _default = {}, std::optional<std::string> _doc = {})
200  : id(std::move(_id)),
201  type(std::move(_type)),
202  optional(_optional),
203  default_(std::move(_default)),
204  doc(std::move(_doc)) {}
205 
206  std::optional<ID> id;
207  OperandType type;
208  bool optional = false;
209  std::optional<Expression> default_ = {};
210  std::optional<std::string> doc;
212  bool operator==(const Operand& other) const {
213  if ( this == &other )
214  return true;
215 
216  if ( ! (std::holds_alternative<Type>(type) && std::holds_alternative<Type>(other.type)) )
217  return false;
218 
219  return std::get<Type>(type) == std::get<Type>(other.type) && id == other.id && optional == other.optional &&
220  default_ == other.default_;
221  }
222 };
223 
224 inline std::ostream& operator<<(std::ostream& out, const Operand& op) {
225  if ( auto t = std::get_if<Type>(&op.type) )
226  out << *t;
227  else
228  out << "<inferred type>";
229 
230  if ( op.id )
231  out << ' ' << *op.id;
232 
233  if ( op.default_ )
234  out << " = " << *op.default_;
235  else if ( op.optional )
236  out << " (optional)";
237 
238  return out;
239 }
240 
241 using ResultType = OperandType;
242 
244 enum Priority { Low, Normal };
245 
254 struct Signature {
255  Type self;
256  bool const_ = true;
257  bool lhs = false;
258  Priority priority = Priority::Low;
259  ResultType result;
260  ID id;
261  std::vector<Operand> args;
262  std::string doc;
263 };
264 
266 enum class Kind {
267  Add,
268  Begin,
269  BitAnd,
270  BitOr,
271  BitXor,
272  Call,
273  Cast,
274  CustomAssign,
275  DecrPostfix,
276  DecrPrefix,
277  Delete,
278  Deref,
279  Difference,
280  DifferenceAssign,
281  Division,
282  DivisionAssign,
283  Equal,
284  End,
285  Greater,
286  GreaterEqual,
287  HasMember,
288  In,
289  IncrPostfix,
290  IncrPrefix,
291  Index,
292  IndexAssign,
293  Lower,
294  LowerEqual,
295  Member,
296  MemberCall,
297  Modulo,
298  Multiple,
299  MultipleAssign,
300  Negate,
301  New,
302  Power,
303  ShiftLeft,
304  ShiftRight,
305  SignNeg,
306  SignPos,
307  Size,
308  Sum,
309  SumAssign,
310  TryMember,
311  Unequal,
312  Unknown,
313  Unpack,
314  Unset
315 };
316 
318 inline auto isCommutative(Kind k) {
319  switch ( k ) {
320  case Kind::BitAnd:
321  case Kind::BitOr:
322  case Kind::BitXor:
323  case Kind::Equal:
324  case Kind::Unequal:
325  case Kind::Multiple:
326  case Kind::Sum: return true;
327 
328  case Kind::Add:
329  case Kind::Begin:
330  case Kind::Call:
331  case Kind::Cast:
332  case Kind::CustomAssign:
333  case Kind::DecrPostfix:
334  case Kind::DecrPrefix:
335  case Kind::Delete:
336  case Kind::Deref:
337  case Kind::Difference:
338  case Kind::DifferenceAssign:
339  case Kind::Division:
340  case Kind::DivisionAssign:
341  case Kind::End:
342  case Kind::Greater:
343  case Kind::GreaterEqual:
344  case Kind::HasMember:
345  case Kind::In:
346  case Kind::IncrPostfix:
347  case Kind::IncrPrefix:
348  case Kind::Index:
349  case Kind::IndexAssign:
350  case Kind::Lower:
351  case Kind::LowerEqual:
352  case Kind::Member:
353  case Kind::MemberCall:
354  case Kind::Modulo:
355  case Kind::MultipleAssign:
356  case Kind::Negate:
357  case Kind::New:
358  case Kind::Power:
359  case Kind::ShiftLeft:
360  case Kind::ShiftRight:
361  case Kind::SignNeg:
362  case Kind::SignPos:
363  case Kind::Size:
364  case Kind::SumAssign:
365  case Kind::TryMember:
366  case Kind::Unknown:
367  case Kind::Unpack:
368  case Kind::Unset: return false;
369  };
370 
371  util::cannot_be_reached();
372 }
373 
374 namespace detail {
375 constexpr util::enum_::Value<Kind> kinds[] = {{Kind::Add, "add"}, {Kind::Begin, "begin"},
376  {Kind::BitAnd, "&"}, {Kind::BitOr, "|"},
377  {Kind::BitXor, "^"}, {Kind::Call, "call"},
378  {Kind::Cast, "cast"}, {Kind::CustomAssign, "="},
379  {Kind::DecrPostfix, "--"}, {Kind::DecrPrefix, "--"},
380  {Kind::Delete, "delete"}, {Kind::Deref, "*"},
381  {Kind::Division, "/"}, {Kind::DivisionAssign, "/="},
382  {Kind::Equal, "=="}, {Kind::End, "end"},
383  {Kind::Greater, ">"}, {Kind::GreaterEqual, ">="},
384  {Kind::HasMember, "?."}, {Kind::In, "in"},
385  {Kind::IncrPostfix, "++"}, {Kind::IncrPrefix, "++"},
386  {Kind::Index, "index"}, {Kind::IndexAssign, "index_assign"},
387  {Kind::Lower, "<"}, {Kind::LowerEqual, "<="},
388  {Kind::Member, "."}, {Kind::MemberCall, "method call"},
389  {Kind::Negate, "~"}, {Kind::New, "new"},
390  {Kind::Difference, "-"}, {Kind::DifferenceAssign, "-="},
391  {Kind::Modulo, "%"}, {Kind::Multiple, "*"},
392  {Kind::MultipleAssign, "*="}, {Kind::Sum, "+"},
393  {Kind::SumAssign, "+="}, {Kind::Power, "**"},
394  {Kind::ShiftLeft, "<<"}, {Kind::ShiftRight, ">>"},
395  {Kind::SignNeg, "-"}, {Kind::SignPos, "+"},
396  {Kind::Size, "size"}, {Kind::TryMember, ".?"},
397  {Kind::Unequal, "!="}, {Kind::Unknown, "<unknown>"},
398  {Kind::Unpack, "unpack"}, {Kind::Unset, "unset"}};
399 } // namespace detail
400 
406 constexpr auto to_string(Kind m) { return util::enum_::to_string(m, detail::kinds); }
407 
408 namespace detail {
409 class Operator;
410 
411 #include <hilti/autogen/__operator.h>
412 
413 } // namespace detail
414 } // namespace operator_
415 
416 using Operator = operator_::detail::Operator;
417 
418 inline bool operator==(const Operator& x, const Operator& y) {
419  if ( &x == &y )
420  return true;
421 
422  return x.typename_() == y.typename_();
423 }
424 
425 } // namespace hilti
Definition: operator.h:191
Definition: node.h:37
OperandType type
Definition: operator.h:207
Definition: util.h:596
Definition: doc-only.h:18
std::optional< std::string > doc
Definition: operator.h:210
Definition: visitor-types.h:28
std::optional< Expression > default_
Definition: operator.h:209
Definition: operator.h:35
ResultType result
Definition: operator.h:259
Definition: type.h:158
ID id
Definition: operator.h:260
std::optional< ID > id
Definition: operator.h:206
std::vector< Operand > args
Definition: operator.h:261
std::string doc
Definition: operator.h:262
Definition: type.h:13
Definition: node.h:21
bool optional
Definition: operator.h:208
Definition: id.h:18
Definition: operator.h:254