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