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::isDereferenceable(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 
233 enum Priority { Low, Normal };
234 
243 struct Signature {
244  Type self;
245  bool const_ = true;
246  bool lhs = false;
247  Priority priority = Priority::Low;
248  ResultType result;
249  ID id;
250  std::vector<Operand> args;
251  std::string doc;
252 };
253 
255 enum class Kind {
256  Add,
257  Begin,
258  BitAnd,
259  BitOr,
260  BitXor,
261  Call,
262  Cast,
263  CustomAssign,
264  DecrPostfix,
265  DecrPrefix,
266  Delete,
267  Deref,
268  Difference,
269  DifferenceAssign,
270  Division,
271  DivisionAssign,
272  Equal,
273  End,
274  Greater,
275  GreaterEqual,
276  HasMember,
277  In,
278  IncrPostfix,
279  IncrPrefix,
280  Index,
281  IndexAssign,
282  Lower,
283  LowerEqual,
284  Member,
285  MemberCall,
286  Modulo,
287  Multiple,
288  MultipleAssign,
289  Negate,
290  New,
291  Power,
292  ShiftLeft,
293  ShiftRight,
294  SignNeg,
295  SignPos,
296  Size,
297  Sum,
298  SumAssign,
299  TryMember,
300  Unequal,
301  Unknown,
302  Unpack,
303  Unset
304 };
305 
307 constexpr auto isCommutative(Kind k) {
308  switch ( k ) {
309  case Kind::BitAnd:
310  case Kind::BitOr:
311  case Kind::BitXor:
312  case Kind::Equal:
313  case Kind::Unequal:
314  case Kind::Multiple:
315  case Kind::Sum: return true;
316 
317  case Kind::Add:
318  case Kind::Begin:
319  case Kind::Call:
320  case Kind::Cast:
321  case Kind::CustomAssign:
322  case Kind::DecrPostfix:
323  case Kind::DecrPrefix:
324  case Kind::Delete:
325  case Kind::Deref:
326  case Kind::Difference:
327  case Kind::DifferenceAssign:
328  case Kind::Division:
329  case Kind::DivisionAssign:
330  case Kind::End:
331  case Kind::Greater:
332  case Kind::GreaterEqual:
333  case Kind::HasMember:
334  case Kind::In:
335  case Kind::IncrPostfix:
336  case Kind::IncrPrefix:
337  case Kind::Index:
338  case Kind::IndexAssign:
339  case Kind::Lower:
340  case Kind::LowerEqual:
341  case Kind::Member:
342  case Kind::MemberCall:
343  case Kind::Modulo:
344  case Kind::MultipleAssign:
345  case Kind::Negate:
346  case Kind::New:
347  case Kind::Power:
348  case Kind::ShiftLeft:
349  case Kind::ShiftRight:
350  case Kind::SignNeg:
351  case Kind::SignPos:
352  case Kind::Size:
353  case Kind::SumAssign:
354  case Kind::TryMember:
355  case Kind::Unknown:
356  case Kind::Unpack:
357  case Kind::Unset: return false;
358  };
359 
361 }
362 
363 namespace detail {
364 constexpr util::enum_::Value<Kind> kinds[] = {{Kind::Add, "add"}, {Kind::Begin, "begin"},
365  {Kind::BitAnd, "&"}, {Kind::BitOr, "|"},
366  {Kind::BitXor, "^"}, {Kind::Call, "call"},
367  {Kind::Cast, "cast"}, {Kind::CustomAssign, "="},
368  {Kind::DecrPostfix, "--"}, {Kind::DecrPrefix, "--"},
369  {Kind::Delete, "delete"}, {Kind::Deref, "*"},
370  {Kind::Division, "/"}, {Kind::DivisionAssign, "/="},
371  {Kind::Equal, "=="}, {Kind::End, "end"},
372  {Kind::Greater, ">"}, {Kind::GreaterEqual, ">="},
373  {Kind::HasMember, "?."}, {Kind::In, "in"},
374  {Kind::IncrPostfix, "++"}, {Kind::IncrPrefix, "++"},
375  {Kind::Index, "index"}, {Kind::IndexAssign, "index_assign"},
376  {Kind::Lower, "<"}, {Kind::LowerEqual, "<="},
377  {Kind::Member, "."}, {Kind::MemberCall, "method call"},
378  {Kind::Negate, "~"}, {Kind::New, "new"},
379  {Kind::Difference, "-"}, {Kind::DifferenceAssign, "-="},
380  {Kind::Modulo, "%"}, {Kind::Multiple, "*"},
381  {Kind::MultipleAssign, "*="}, {Kind::Sum, "+"},
382  {Kind::SumAssign, "+="}, {Kind::Power, "**"},
383  {Kind::ShiftLeft, "<<"}, {Kind::ShiftRight, ">>"},
384  {Kind::SignNeg, "-"}, {Kind::SignPos, "+"},
385  {Kind::Size, "size"}, {Kind::TryMember, ".?"},
386  {Kind::Unequal, "!="}, {Kind::Unknown, "<unknown>"},
387  {Kind::Unpack, "unpack"}, {Kind::Unset, "unset"}};
388 } // namespace detail
389 
395 constexpr auto to_string(Kind m) { return util::enum_::to_string(m, detail::kinds); }
396 
397 namespace detail {
398 class Operator;
399 
400 #include <hilti/autogen/__operator.h>
401 
402 } // namespace detail
403 } // namespace operator_
404 
405 using Operator = operator_::detail::Operator;
406 
407 inline bool operator==(const Operator& x, const Operator& y) {
408  if ( &x == &y )
409  return true;
410 
411  return x.typename_() == y.typename_();
412 }
413 
414 } // 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:248
Definition: type.h:159
void cannot_be_reached() __attribute__((noreturn))
Definition: util.cc:33
ID id
Definition: operator.h:249
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:250
std::string doc
Definition: operator.h:251
Definition: type.h:14
Definition: node.h:22
bool optional
Definition: operator.h:197
Definition: id.h:18
Definition: operator.h:243