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