Spicy
tuple.h
1 // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <memory>
6 #include <string>
7 #include <vector>
8 
9 #include <hilti/ast/ctors/tuple.h>
10 #include <hilti/ast/operators/common.h>
11 #include <hilti/ast/types/tuple.h>
12 
13 namespace hilti {
14 namespace operator_ {
15 
16 STANDARD_OPERATOR_2(tuple, Equal, type::Bool(), type::constant(type::Tuple(type::Wildcard())),
17  operator_::sameTypeAs(0, "tuple<*>"), "Compares two tuples element-wise.");
18 STANDARD_OPERATOR_2(tuple, Unequal, type::Bool(), type::constant(type::Tuple(type::Wildcard())),
19  operator_::sameTypeAs(0, "tuple<*>"), "Compares two tuples element-wise.");
20 
21 BEGIN_OPERATOR_CUSTOM(tuple, Index)
22  Type result(const hilti::node::Range<Expression>& ops) const {
23  if ( ops.empty() )
24  return type::DocOnly("<type of element>");
25 
26  if ( ops.size() < 2 )
27  return type::unknown;
28 
29  auto ctor = ops[1].tryAs<expression::Ctor>();
30  if ( ! ctor )
31  return type::unknown;
32 
33  auto i = ctor->ctor().tryAs<ctor::UnsignedInteger>();
34  if ( ! i )
35  return type::unknown;
36 
37  const auto& elements = ops[0].type().as<type::Tuple>().elements();
38 
39  if ( static_cast<uint64_t>(elements.size()) <= i->value() )
40  return type::unknown;
41 
42  return elements[i->value()].type();
43  }
44 
45  bool isLhs() const { return true; }
46  auto priority() const { return hilti::operator_::Priority::Normal; }
47 
48  std::vector<Operand> operands() const {
49  return {{.type = type::Tuple(type::Wildcard())}, {.type = type::UnsignedInteger(64)}};
50  }
51 
52  void validate(const expression::ResolvedOperator& i, operator_::position_t p) const {
53  if ( auto ec = i.op1().tryAs<expression::Ctor>() )
54  if ( auto c = ec->ctor().tryAs<ctor::UnsignedInteger>() ) {
55  if ( c->value() < 0 ||
56  c->value() >= static_cast<uint64_t>(i.op0().type().as<type::Tuple>().elements().size()) )
57  p.node.addError("tuple index out of range");
58 
59  return;
60  }
61 
62  p.node.addError("tuple index must be an integer constant");
63  }
64 
65  std::string doc() const {
66  return "Extracts the tuple element at the given index. The index must be a constant unsigned integer.";
67  }
68 END_OPERATOR_CUSTOM
69 
70 BEGIN_OPERATOR_CUSTOM(tuple, Member)
71  Type result(const hilti::node::Range<Expression>& ops) const {
72  if ( ops.empty() )
73  return type::DocOnly("<type of element>");
74 
75  auto id = ops[1].as<expression::Member>().id();
76  auto elem = ops[0].type().as<type::Tuple>().elementByID(id);
77  if ( ! elem )
78  return type::unknown;
79 
80  return elem->second->type();
81  }
82 
83  bool isLhs() const { return true; }
84  auto priority() const { return hilti::operator_::Priority::Normal; }
85 
86  std::vector<Operand> operands() const {
87  return {{.type = type::Tuple(type::Wildcard())}, {.type = type::Member(type::Wildcard()), .doc = "<id>"}};
88  }
89 
90  void validate(const expression::ResolvedOperator& i, operator_::position_t p) const {
91  auto id = i.operands()[1].as<expression::Member>().id();
92  auto elem = i.operands()[0].type().as<type::Tuple>().elementByID(id);
93 
94  if ( ! elem )
95  p.node.addError("unknown tuple element");
96  }
97 
98  std::string doc() const { return "Extracts the tuple element corresponding to the given ID."; }
99 END_OPERATOR_CUSTOM_x
100 
101 BEGIN_OPERATOR_CUSTOM(tuple, CustomAssign)
102  Type result(const hilti::node::Range<Expression>& ops) const {
103  if ( ops.empty() )
104  return type::DocOnly("<tuple>");
105 
106  return ops[0].type();
107  }
108 
109  bool isLhs() const { return false; }
110  auto priority() const { return hilti::operator_::Priority::Normal; }
111 
112  std::vector<Operand> operands() const {
113  // The operator gets instantiated only through the normalizer, but this is used for documentation.
114  return {{
115  .type = type::Member(type::Wildcard()),
116  .doc = "(x, ..., y)",
117  },
118  {.type = type::Tuple(type::Wildcard()), .doc = "<tuple>"}};
119  return {};
120  }
121 
122  void validate(const expression::ResolvedOperator& i, operator_::position_t p) const {
123  auto lhs = i.operands()[0].as<expression::Ctor>().ctor().as<ctor::Tuple>();
124  auto lhs_type = lhs.type().as<type::Tuple>();
125  auto rhs_type = i.operands()[1].type().tryAs<type::Tuple>();
126  if ( ! rhs_type ) {
127  p.node.addError("rhs is not a tuple");
128  return;
129  }
130 
131  if ( lhs_type.elements().size() != rhs_type->elements().size() ) {
132  p.node.addError("cannot assign tuples of different length");
133  return;
134  }
135 
136  for ( auto i = 0u; i < lhs_type.elements().size(); i++ ) {
137  const auto& lhs_elem = lhs.value()[i];
138  const auto& lhs_elem_type = lhs_type.elements()[i].type();
139  const auto& rhs_elem_type = rhs_type->elements()[i].type();
140 
141  if ( ! lhs_elem.isLhs() )
142  p.node.addError(util::fmt("cannot assign to expression: %s", to_node(lhs_elem)));
143 
144  if ( ! type::sameExceptForConstness(lhs_elem_type, rhs_elem_type) )
145  p.node.addError(util::fmt("type mismatch for element %d in assignment, expected type %s but got %s", i,
146  lhs_elem_type, rhs_elem_type));
147  }
148  }
149 
150  std::string doc() const { return "Assigns element-wise to the left-hand-side tuple"; }
151 END_OPERATOR_CUSTOM_x
152 
153 } // namespace operator_
154 } // namespace hilti
Definition: node.h:39
std::string fmt(const char *fmt, const Args &... args)
Definition: util.h:80