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