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 
71  std::vector<Operand> operands() const {
72  return {{.type = type::constant(type::Union(type::Wildcard())), .doc = "union"},
73  {.type = type::Member(type::Wildcard()), .doc = "<field>"}};
74  }
75 
76  void validate(const expression::ResolvedOperator& i, position_t p) const {
77  detail::checkName(i.op0(), i.op1(), p.node);
78  }
79 
80  std::string doc() const {
81  return R"(
82 Retrieves the value of a union's field. If the union does not have the field set,
83 this triggers an exception.
84 )";
85  }
86 END_OPERATOR_CUSTOM_x
87 
88 BEGIN_OPERATOR_CUSTOM_x(union_, MemberNonConst, Member)
89  Type result(const hilti::node::Range<Expression>& ops) const {
90  if ( ops.empty() )
91  return type::DocOnly("<field type>");
92 
93  return detail::itemType(ops[0], ops[1]);
94  }
95 
96  bool isLhs() const { return true; }
97 
98  std::vector<Operand> operands() const {
99  return {{.type = type::Union(type::Wildcard()), .doc = "union"},
100  {.type = type::Member(type::Wildcard()), .doc = "<field>"}};
101  }
102 
103  void validate(const expression::ResolvedOperator& i, position_t p) const {
104  detail::checkName(i.op0(), i.op1(), p.node);
105  }
106 
107  std::string doc() const {
108  return R"(
109 Retrieves the value of a union's field. If the union does not have the field set,
110 this triggers an exception unless the value is only being assigned to.
111 )";
112  }
113 END_OPERATOR_CUSTOM_x
114 
115 BEGIN_OPERATOR_CUSTOM(union_, HasMember)
116  Type result(const hilti::node::Range<Expression>& /* ops */) const { return type::Bool(); }
117 
118  bool isLhs() const { return false; }
119 
120  std::vector<Operand> operands() const {
121  return {{.type = type::Union(type::Wildcard()), .doc = "union"},
122  {.type = type::Member(type::Wildcard()), .doc = "<field>"}};
123  }
124 
125  void validate(const expression::ResolvedOperator& i, position_t p) const {
126  detail::checkName(i.op0(), i.op1(), p.node);
127  }
128 
129  std::string doc() const { return "Returns true if the union's field is set."; }
130 END_OPERATOR_CUSTOM
131 
132 } // namespace operator_
133 } // namespace hilti
Definition: node.h:39
std::string fmt(const char *fmt, const Args &... args)
Definition: util.h:80