Spicy
union.h
1 // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <string>
6 #include <utility>
7 #include <vector>
8 
9 #include <hilti/ast/expressions/member.h>
10 #include <hilti/ast/operators/common.h>
11 #include <hilti/ast/types/any.h>
12 #include <hilti/ast/types/union.h>
13 #include <hilti/ast/types/unknown.h>
14 
15 namespace hilti::operator_ {
16 
17 namespace union_::detail {
18 
19 // Returns an operand as a member expression.
20 static expression::Member memberExpression(const Expression& op) {
21  if ( auto c = op.tryAs<expression::Coerced>() )
22  return c->expression().as<expression::Member>();
23 
24  return op.as<expression::Member>();
25 }
26 
27 // Checks if an operand refers to a valid field inside a union.
28 static inline void checkName(const Expression& op0, const Expression& op1, Node& n) {
29  auto id = memberExpression(op1).id().local();
30 
31  if ( auto f = op0.type().as<type::Union>().field(id); ! f )
32  n.addError(util::fmt("type does not have field '%s'", id));
33 }
34 
35 // Returns the type of a union field referenced by an operand.
36 static inline Type itemType(const Expression& op0, const Expression& op1) {
37  if ( auto st = op0.type().tryAs<type::Union>() ) {
38  if ( auto f = st->field(memberExpression(op1).id().local()) )
39  return f->type();
40  }
41 
42  return type::unknown;
43 }
44 
45 // Returns the result type of a union method referenced by an operand.
46 static inline Type methodResult(const Expression& /* op0 */, const Expression& op1) {
47  if ( auto f = memberExpression(op1).type().template tryAs<type::Function>() )
48  return f->result().type();
49 
50  return type::unknown;
51 }
52 
53 } // namespace union_::detail
54 
55 STANDARD_OPERATOR_2(union_, Equal, type::Bool(), type::constant(type::Union(type::Wildcard())),
56  operator_::sameTypeAs(0, "union<*>"), "Compares two unions element-wise.");
57 STANDARD_OPERATOR_2(union_, Unequal, type::Bool(), type::constant(type::Union(type::Wildcard())),
58  operator_::sameTypeAs(0, "union<*>"), "Compares two unions element-wise.");
59 
60 BEGIN_OPERATOR_CUSTOM_x(union_, MemberConst, Member)
61  Type result(const hilti::node::Range<Expression>& ops) const {
62  if ( ops.empty() )
63  return type::DocOnly("<field type>");
64 
65  return detail::itemType(ops[0], ops[1]);
66  }
67 
68  bool isLhs() const { return false; }
69  auto priority() const { return hilti::operator_::Priority::Normal; }
70 
71  const std::vector<Operand>& operands() const {
72  static std::vector<Operand> _operands =
73  {{{}, type::constant(type::Union(type::Wildcard())), false, {}, "union"},
74  {{}, type::Member(type::Wildcard()), false, {}, "<field>"}};
75  return _operands;
76  }
77 
78  void validate(const expression::ResolvedOperator& i, position_t p) const {
79  detail::checkName(i.op0(), i.op1(), p.node);
80  }
81 
82  std::string doc() const {
83  return R"(
84 Retrieves the value of a union's field. If the union does not have the field set,
85 this triggers an exception.
86 )";
87  }
88 END_OPERATOR_CUSTOM_x
89 
90 BEGIN_OPERATOR_CUSTOM_x(union_, MemberNonConst, Member)
91  Type result(const hilti::node::Range<Expression>& ops) const {
92  if ( ops.empty() )
93  return type::DocOnly("<field type>");
94 
95  return detail::itemType(ops[0], ops[1]);
96  }
97 
98  bool isLhs() const { return true; }
99  auto priority() const { return hilti::operator_::Priority::Normal; }
100 
101  const std::vector<Operand>& operands() const {
102  static std::vector<Operand> _operands = {{{}, type::Union(type::Wildcard()), false, {}, "union"},
103  {{}, type::Member(type::Wildcard()), false, {}, "<field>"}};
104  return _operands;
105  }
106 
107  void validate(const expression::ResolvedOperator& i, position_t p) const {
108  detail::checkName(i.op0(), i.op1(), p.node);
109  }
110 
111  std::string doc() const {
112  return R"(
113 Retrieves the value of a union's field. If the union does not have the field set,
114 this triggers an exception unless the value is only being assigned to.
115 )";
116  }
117 END_OPERATOR_CUSTOM_x
118 
119 BEGIN_OPERATOR_CUSTOM(union_, HasMember)
120  Type result(const hilti::node::Range<Expression>& /* ops */) const { return type::Bool(); }
121 
122  bool isLhs() const { return false; }
123  auto priority() const { return hilti::operator_::Priority::Normal; }
124 
125  const std::vector<Operand>& operands() const {
126  static std::vector<Operand> _operands = {{{}, type::Union(type::Wildcard()), false, {}, "union"},
127  {{}, type::Member(type::Wildcard()), false, {}, "<field>"}};
128  return _operands;
129  }
130 
131  void validate(const expression::ResolvedOperator& i, position_t p) const {
132  detail::checkName(i.op0(), i.op1(), p.node);
133  }
134 
135  std::string doc() const { return "Returns true if the union's field is set."; }
136 END_OPERATOR_CUSTOM
137 
138 } // namespace hilti::operator_
Definition: node.h:38
Definition: operator-registry.h:15