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 
47  std::vector<Operand> operands() const {
48  return {{.type = type::Tuple(type::Wildcard())}, {.type = type::UnsignedInteger(64)}};
49  }
50 
51  void validate(const expression::ResolvedOperator& i, operator_::position_t p) const {
52  if ( auto ec = i.op1().tryAs<expression::Ctor>() )
53  if ( auto c = ec->ctor().tryAs<ctor::UnsignedInteger>() ) {
54  if ( c->value() < 0 ||
55  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 elem = ops[0].type().as<type::Tuple>().elementByID(id);
76  if ( ! elem )
77  return type::unknown;
78 
79  return elem->second->type();
80  }
81 
82  bool isLhs() const { return true; }
83 
84  std::vector<Operand> operands() const {
85  return {{.type = type::Tuple(type::Wildcard())}, {.type = type::Member(type::Wildcard()), .doc = "<id>"}};
86  }
87 
88  void validate(const expression::ResolvedOperator& i, operator_::position_t p) const {
89  auto id = i.operands()[1].as<expression::Member>().id();
90  auto elem = i.operands()[0].type().as<type::Tuple>().elementByID(id);
91 
92  if ( ! elem )
93  p.node.addError("unknown tuple element");
94  }
95 
96  std::string doc() const { return "Extracts the tuple element corresponding to the given ID."; }
97 END_OPERATOR_CUSTOM_x
98 
99 BEGIN_OPERATOR_CUSTOM(tuple, CustomAssign)
100  Type result(const hilti::node::Range<Expression>& ops) const {
101  if ( ops.empty() )
102  return type::DocOnly("<tuple>");
103 
104  return ops[0].type();
105  }
106 
107  bool isLhs() const { return false; }
108 
109  std::vector<Operand> operands() const {
110  // The operator gets instantiated only through the normalizer, but this is used for documentation.
111  return {{
112  .type = type::Member(type::Wildcard()),
113  .doc = "(x, ..., y)",
114  },
115  {.type = type::Tuple(type::Wildcard()), .doc = "<tuple>"}};
116  return {};
117  }
118 
119  void validate(const expression::ResolvedOperator& i, operator_::position_t p) const {
120  auto lhs = i.operands()[0].as<expression::Ctor>().ctor().as<ctor::Tuple>();
121  auto lhs_type = lhs.type().as<type::Tuple>();
122  auto rhs_type = i.operands()[1].type().tryAs<type::Tuple>();
123  if ( ! rhs_type ) {
124  p.node.addError("rhs is not a tuple");
125  return;
126  }
127 
128  if ( lhs_type.elements().size() != rhs_type->elements().size() ) {
129  p.node.addError("cannot assign tuples of different length");
130  return;
131  }
132 
133  for ( auto i = 0u; i < lhs_type.elements().size(); i++ ) {
134  const auto& lhs_elem = lhs.value()[i];
135  const auto& lhs_elem_type = lhs_type.elements()[i].type();
136  const auto& rhs_elem_type = rhs_type->elements()[i].type();
137 
138  if ( ! lhs_elem.isLhs() )
139  p.node.addError(util::fmt("cannot assign to expression: %s", to_node(lhs_elem)));
140 
141  if ( ! type::sameExceptForConstness(lhs_elem_type, rhs_elem_type) )
142  p.node.addError(util::fmt("type mismatch for element %d in assignment, expected type %s but got %s", i,
143  lhs_elem_type, rhs_elem_type));
144  }
145  }
146 
147  std::string doc() const { return "Assigns element-wise to the left-hand-side tuple"; }
148 END_OPERATOR_CUSTOM_x
149 
150 } // namespace operator_
151 } // namespace hilti
Definition: node.h:39
std::string fmt(const char *fmt, const Args &... args)
Definition: util.h:80