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