souffle  2.0.2-371-g6315b36
FunctorOps.cpp
Go to the documentation of this file.
1 
2 #include "FunctorOps.h"
5 #include <cassert>
6 #include <cctype>
7 #include <map>
8 #include <memory>
9 #include <ostream>
10 #include <utility>
11 #include <vector>
12 
13 namespace souffle {
14 
15 namespace {
16 char const* functorOpNameLegacy(FunctorOp op) {
17  switch (op) {
18  /** Unary Functor Operators */
19  case FunctorOp::ORD: return "ord";
20  case FunctorOp::STRLEN: return "strlen";
21  case FunctorOp::NEG:
22  case FunctorOp::FNEG: return "-";
23  case FunctorOp::BNOT:
24  case FunctorOp::UBNOT: return "bnot";
25  case FunctorOp::LNOT:
26  case FunctorOp::ULNOT: return "lnot";
27  case FunctorOp::I2F:
28  case FunctorOp::S2F:
29  case FunctorOp::U2F: return "to_float";
30  case FunctorOp::F2I:
31  case FunctorOp::S2I:
32  case FunctorOp::U2I: return "to_number";
33  case FunctorOp::I2S:
34  case FunctorOp::F2S:
35  case FunctorOp::U2S: return "to_string";
36  case FunctorOp::F2U:
37  case FunctorOp::I2U:
38  case FunctorOp::S2U: return "to_unsigned";
39 
40  /** Binary Functor Operators */
41  case FunctorOp::ADD:
42  case FunctorOp::FADD:
43  case FunctorOp::UADD: return "+";
44  case FunctorOp::SUB:
45  case FunctorOp::USUB:
46  case FunctorOp::FSUB: return "-";
47  case FunctorOp::MUL:
48  case FunctorOp::UMUL:
49  case FunctorOp::FMUL: return "*";
50  case FunctorOp::DIV:
51  case FunctorOp::UDIV:
52  case FunctorOp::FDIV: return "/";
53  case FunctorOp::EXP:
54  case FunctorOp::FEXP:
55  case FunctorOp::UEXP: return "^";
56  case FunctorOp::MOD:
57  case FunctorOp::UMOD: return "%";
58  case FunctorOp::BAND:
59  case FunctorOp::UBAND: return "band";
60  case FunctorOp::BOR:
61  case FunctorOp::UBOR: return "bor";
62  case FunctorOp::BXOR:
63  case FunctorOp::UBXOR: return "bxor";
65  case FunctorOp::UBSHIFT_L: return "bshl";
67  case FunctorOp::UBSHIFT_R: return "bshr";
69  case FunctorOp::UBSHIFT_R_UNSIGNED: return "bshru";
70  case FunctorOp::LAND:
71  case FunctorOp::ULAND: return "land";
72  case FunctorOp::LOR:
73  case FunctorOp::ULOR: return "lor";
74  case FunctorOp::LXOR:
75  case FunctorOp::ULXOR: return "lxor";
76  case FunctorOp::RANGE:
77  case FunctorOp::URANGE:
78  case FunctorOp::FRANGE: return "range";
79 
80  /* N-ary Functor Operators */
81  case FunctorOp::MAX:
82  case FunctorOp::UMAX:
83  case FunctorOp::FMAX:
84  case FunctorOp::SMAX: return "max";
85  case FunctorOp::MIN:
86  case FunctorOp::UMIN:
87  case FunctorOp::FMIN:
88  case FunctorOp::SMIN: return "min";
89  case FunctorOp::CAT: return "cat";
90 
91  /** Ternary Functor Operators */
92  case FunctorOp::SUBSTR: return "substr";
93  }
94 
96 }
97 
98 char const* functorOpNameSymbol(FunctorOp op) {
99  switch (op) {
100  /** Unary Functor Operators */
101  case FunctorOp::NEG:
103  case FunctorOp::BNOT:
104  case FunctorOp::UBNOT: return "~";
105  case FunctorOp::LNOT:
106  case FunctorOp::ULNOT: return "!";
107  case FunctorOp::EXP:
108  case FunctorOp::FEXP:
109  case FunctorOp::UEXP: return "**";
110  case FunctorOp::BAND:
111  case FunctorOp::UBAND: return "&";
112  case FunctorOp::BOR:
113  case FunctorOp::UBOR: return "|";
114  case FunctorOp::BXOR:
115  case FunctorOp::UBXOR: return "^";
116  case FunctorOp::BSHIFT_L:
117  case FunctorOp::UBSHIFT_L: return "<<";
118  case FunctorOp::BSHIFT_R:
119  case FunctorOp::UBSHIFT_R: return ">>";
121  case FunctorOp::UBSHIFT_R_UNSIGNED: return ">>>";
122  case FunctorOp::LAND:
123  case FunctorOp::ULAND: return "&&";
124  case FunctorOp::LOR:
125  case FunctorOp::ULOR: return "||";
126  case FunctorOp::LXOR:
127  case FunctorOp::ULXOR: return "^^";
128 
129  default: return functorOpNameLegacy(op);
130  }
131 }
132 
133 using TAttr = TypeAttribute;
134 using FOp = FunctorOp;
135 #define OP_1(op, t0, tDst) \
136  { functorOpNameSymbol(FOp::op), {TAttr::t0}, TAttr::tDst, FOp::op }
137 #define OP_2(op, t0, t1, tDst, multi) \
138  { functorOpNameSymbol(FOp::op), {TAttr::t0, TAttr::t1}, TAttr::tDst, FOp::op, false, multi }
139 #define OP_3(op, t0, t1, t2, tDst, multi) \
140  { functorOpNameSymbol(FOp::op), {TAttr::t0, TAttr::t1, TAttr::t2}, TAttr::tDst, FOp::op, false, multi }
141 
142 #define OP_1_MONO(op, ty) OP_1(op, ty, ty)
143 #define OP_2_MONO(op, ty, multi) OP_2(op, ty, ty, ty, multi)
144 #define OP_3_MONO(op, ty, multi) OP_3(op, ty, ty, ty, ty, multi)
145 #define OP_1_INTEGRAL(op) OP_1_MONO(op, Signed), OP_1_MONO(U##op, Unsigned)
146 #define OP_2_INTEGRAL_EX(op, multi) OP_2_MONO(op, Signed, multi), OP_2_MONO(U##op, Unsigned, multi)
147 #define OP_3_INTEGRAL_EX(op, multi) OP_3_MONO(op, Signed, multi), OP_3_MONO(U##op, Unsigned, multi)
148 #define OP_2_INTEGRAL(op) OP_2_INTEGRAL_EX(op, false)
149 #define OP_2_NUMERIC(op) OP_2_MONO(F##op, Float, false), OP_2_INTEGRAL(op)
150 #define OP_2_NUMERIC_MULTI(op) OP_2_MONO(F##op, Float, true), OP_2_INTEGRAL_EX(op, true)
151 #define OP_3_NUMERIC_MULTI(op) OP_3_MONO(F##op, Float, true), OP_3_INTEGRAL_EX(op, true)
152 #define VARIADIC(op, ty) \
153  { functorOpNameLegacy(FOp::op), {TAttr::ty}, TAttr::ty, FOp::op, true }
154 #define VARIADIC_ORDERED(op) \
155  VARIADIC(op, Signed), VARIADIC(U##op, Unsigned), VARIADIC(F##op, Float), VARIADIC(S##op, Symbol)
156 
157 const std::vector<IntrinsicFunctorInfo> FUNCTOR_INTRINSICS = {
158  {FUNCTOR_INTRINSIC_PREFIX_NEGATE_NAME, {TAttr::Signed}, TAttr::Signed, FunctorOp::NEG},
159  {FUNCTOR_INTRINSIC_PREFIX_NEGATE_NAME, {TAttr::Float}, TAttr::Float, FunctorOp::FNEG},
160 
161  OP_1(F2I, Float, Signed),
162  OP_1(F2S, Float, Symbol),
163  OP_1(F2U, Float, Unsigned),
164 
165  OP_1(I2F, Signed, Float),
166  OP_1(I2S, Signed, Symbol),
167  OP_1(I2U, Signed, Unsigned),
168 
169  OP_1(S2F, Symbol, Float),
170  OP_1(S2I, Symbol, Signed),
171  OP_1(S2U, Symbol, Unsigned),
172 
173  OP_1(U2F, Unsigned, Float),
174  OP_1(U2I, Unsigned, Signed),
175  OP_1(U2S, Unsigned, Symbol),
176 
177  OP_2_NUMERIC(ADD),
178  OP_2_NUMERIC(SUB),
179  OP_2_NUMERIC(MUL),
180  OP_2_NUMERIC(DIV),
181  OP_2_INTEGRAL(MOD),
182  OP_2_NUMERIC(EXP),
183 
184  OP_2_INTEGRAL(LAND),
185  OP_1_INTEGRAL(LNOT),
186  OP_2_INTEGRAL(LOR),
187  OP_2_INTEGRAL(LXOR),
188 
189  OP_2_INTEGRAL(BAND),
190  OP_1_INTEGRAL(BNOT),
191  OP_2_INTEGRAL(BOR),
192  OP_2_INTEGRAL(BSHIFT_L),
193  OP_2_INTEGRAL(BSHIFT_R),
194  OP_2_INTEGRAL(BSHIFT_R_UNSIGNED),
195  OP_2_INTEGRAL(BXOR),
196 
197  OP_2_NUMERIC_MULTI(RANGE),
198  OP_3_NUMERIC_MULTI(RANGE),
199 
200  VARIADIC_ORDERED(MAX),
201  VARIADIC_ORDERED(MIN),
202 
203  // `ord` is a weird functor that exposes the internal raw value of any type.
204  OP_1(ORD, Signed, Signed),
205  OP_1(ORD, Unsigned, Signed),
206  OP_1(ORD, Float, Signed),
207  OP_1(ORD, Symbol, Signed),
208  OP_1(ORD, Record, Signed),
209 
210  VARIADIC(CAT, Symbol),
211  OP_1(STRLEN, Symbol, Signed),
212  OP_3(SUBSTR, Symbol, Signed, Signed, Symbol, false),
213 };
214 
215 template <typename F>
216 IntrinsicFunctors pickFunctors(F&& f) {
218  for (auto&& x : FUNCTOR_INTRINSICS)
219  if (f(x)) xs.push_back(x);
220  return xs;
221 }
222 } // namespace
223 
225  return pickFunctors([&](auto&& x) { return x.op == op; });
226 }
227 
228 IntrinsicFunctors functorBuiltIn(std::string_view symbol) {
229  return pickFunctors([&](auto&& x) { return x.symbol == symbol; });
230 }
231 
232 IntrinsicFunctors functorBuiltIn(std::string_view symbol, const std::vector<TypeAttribute>& params) {
233  return pickFunctors([&](auto&& x) {
234  auto paramsOk =
235  x.variadic ? all_of(params, [&](auto t) { return t == x.params[0]; }) : x.params == params;
236  return x.symbol == symbol && paramsOk;
237  });
238 }
239 
240 bool isValidFunctorOpArity(std::string_view symbol, size_t arity) {
241  return any_of(FUNCTOR_INTRINSICS, [&](auto&& x) {
242  auto arityOk = x.params.size() == arity || x.variadic;
243  return x.symbol == symbol && arityOk;
244  });
245 }
246 
248  auto&& xs = functorBuiltIn(op);
249  return xs.front().get().multipleResults;
250 }
251 
252 bool isInfixFunctorOp(std::string_view symbol) {
253  assert(!symbol.empty() && "no functors have an empty name");
254  // arithmetic/bitwise/logical negation are prefix ops
255  if (symbol == FUNCTOR_INTRINSIC_PREFIX_NEGATE_NAME) return false;
256  if (symbol == "!") return false;
257  if (symbol == "~") return false;
258  auto alphaUnderscore = symbol == "_" || isalpha(symbol.at(0));
259  return !alphaUnderscore;
260 }
261 
262 bool isInfixFunctorOp(const FunctorOp op) {
263  auto&& xs = functorBuiltIn(op);
264  return isInfixFunctorOp(xs.front().get().symbol);
265 }
266 
267 FunctorOp getMinOp(const std::string& type) {
268  switch (type[0]) {
269  case 'f': return FunctorOp::FMIN;
270  case 'u': return FunctorOp::UMIN;
271  case 'i': return FunctorOp::MIN;
272  default: return FunctorOp::MIN;
273  }
274 }
275 
276 FunctorOp getMaxOp(const std::string& type) {
277  switch (type[0]) {
278  case 'f': return FunctorOp::FMAX;
279  case 'u': return FunctorOp::UMAX;
280  case 'i': return FunctorOp::MAX;
281  default: return FunctorOp::MAX;
282  }
283 }
284 
285 std::ostream& operator<<(std::ostream& os, FunctorOp op) {
286  return os << functorOpNameLegacy(op);
287 }
288 
289 // must defined after `FUNCTOR_INTRINSICS` to ensure correct ctor ordering.
290 #ifndef NDEBUG
291 namespace {
292 struct FUNCTOR_INTRINSIC_SANCHECKER {
293  FUNCTOR_INTRINSIC_SANCHECKER() {
294  std::map<FunctorOp, IntrinsicFunctors> byOp;
295  for (auto&& x : FUNCTOR_INTRINSICS) {
296  byOp[x.op].push_back(x);
297  assert((!x.variadic || x.params.size() == 1) && "variadics must have a single parameter");
298  }
299 
300  for (auto&& [_, xs] : byOp) {
301  auto&& multiResult = xs.front().get().multipleResults;
302  auto&& symbol = xs.front().get().symbol;
303  for (auto&& x : xs) {
304  // this can be relaxed but would require removing the `isFunctorMultiResult : FunctorOp`
305  // overload
306  assert(x.get().multipleResults == multiResult &&
307  "all overloads for op must have same `multipleResults`");
308  // this can be relaxed but would require removing the `isInfixFunctorOp : FunctorOp` overload
309  assert(x.get().symbol == symbol && "all overloads for op must have same `symbol`");
310  }
311  }
312  }
313 } FUNCTOR_INTRINSIC_SANCHECKER;
314 } // namespace
315 #endif
316 
317 } // namespace souffle
souffle::FunctorOp::DIV
@ DIV
OP_2_NUMERIC
#define OP_2_NUMERIC(op)
Definition: FunctorOps.cpp:149
souffle::FunctorOp::UEXP
@ UEXP
souffle::FunctorOp::UBOR
@ UBOR
souffle::FunctorOp::U2I
@ U2I
souffle::FunctorOp::FDIV
@ FDIV
souffle::FunctorOp::UBSHIFT_R
@ UBSHIFT_R
souffle::FunctorOp::I2S
@ I2S
UNREACHABLE_BAD_CASE_ANALYSIS
#define UNREACHABLE_BAD_CASE_ANALYSIS
Definition: MiscUtil.h:206
souffle::FunctorOp::UBXOR
@ UBXOR
souffle::FunctorOp::FMIN
@ FMIN
souffle::FunctorOp::UBSHIFT_R_UNSIGNED
@ UBSHIFT_R_UNSIGNED
souffle::FunctorOp::FADD
@ FADD
souffle::getMaxOp
FunctorOp getMaxOp(const std::string &type)
Definition: FunctorOps.cpp:276
souffle::FunctorOp::FMUL
@ FMUL
souffle::FunctorOp::USUB
@ USUB
souffle::FunctorOp::URANGE
@ URANGE
souffle::FunctorOp::ULAND
@ ULAND
souffle::FunctorOp::EXP
@ EXP
souffle::FunctorOp::UMAX
@ UMAX
MiscUtil.h
souffle::FunctorOp::STRLEN
@ STRLEN
VARIADIC
#define VARIADIC(op, ty)
Definition: FunctorOps.cpp:152
souffle::isFunctorMultiResult
bool isFunctorMultiResult(FunctorOp op)
Definition: FunctorOps.cpp:247
souffle::FunctorOp::NEG
@ NEG
souffle::FunctorOp::ORD
@ ORD
Unary Functor Operators.
souffle::FunctorOp::U2F
@ U2F
souffle::FunctorOp::FNEG
@ FNEG
souffle::FunctorOp::LNOT
@ LNOT
souffle::FunctorOp::BXOR
@ BXOR
souffle::FunctorOp::MOD
@ MOD
souffle::FunctorOp::BSHIFT_L
@ BSHIFT_L
souffle::IntrinsicFunctors
std::vector< std::reference_wrapper< const IntrinsicFunctorInfo > > IntrinsicFunctors
Definition: FunctorOps.h:129
souffle::FunctorOp::UBNOT
@ UBNOT
souffle::FunctorOp::LOR
@ LOR
souffle::FunctorOp::FRANGE
@ FRANGE
souffle::getMinOp
FunctorOp getMinOp(const std::string &type)
Given a type of an an attribute it returns the appropriate min/max functor operation.
Definition: FunctorOps.cpp:267
souffle::FunctorOp::CAT
@ CAT
souffle::FunctorOp::UBSHIFT_L
@ UBSHIFT_L
souffle::FunctorOp::UMUL
@ UMUL
souffle::FunctorOp::ULOR
@ ULOR
OP_3_NUMERIC_MULTI
#define OP_3_NUMERIC_MULTI(op)
Definition: FunctorOps.cpp:151
OP_1_INTEGRAL
#define OP_1_INTEGRAL(op)
Definition: FunctorOps.cpp:145
souffle::FunctorOp::U2S
@ U2S
souffle::FunctorOp::RANGE
@ RANGE
souffle::any_of
bool any_of(const Container &c, UnaryPredicate p)
A generic test checking whether any elements within a container satisfy a certain predicate.
Definition: FunctionalUtil.h:124
souffle::isValidFunctorOpArity
bool isValidFunctorOpArity(std::string_view symbol, size_t arity)
Definition: FunctorOps.cpp:240
OP_2_NUMERIC_MULTI
#define OP_2_NUMERIC_MULTI(op)
Definition: FunctorOps.cpp:150
souffle::FunctorOp::LAND
@ LAND
OP_2_INTEGRAL
#define OP_2_INTEGRAL(op)
Definition: FunctorOps.cpp:148
OP_1
#define OP_1(op, t0, tDst)
Definition: FunctorOps.cpp:135
VARIADIC_ORDERED
#define VARIADIC_ORDERED(op)
Definition: FunctorOps.cpp:154
souffle::FunctorOp::SMAX
@ SMAX
souffle::FunctorOp::I2F
@ I2F
souffle::FunctorOp::BOR
@ BOR
souffle::FunctorOp::BNOT
@ BNOT
souffle::FunctorOp::FEXP
@ FEXP
souffle::FunctorOp::S2I
@ S2I
souffle::operator<<
std::ostream & operator<<(std::ostream &os, AggregateOp op)
Definition: AggregateOp.h:51
souffle::FunctorOp
FunctorOp
Definition: FunctorOps.h:35
souffle::FunctorOp::UMIN
@ UMIN
souffle::FunctorOp::F2I
@ F2I
souffle::functorBuiltIn
IntrinsicFunctors functorBuiltIn(FunctorOp op)
Definition: FunctorOps.cpp:224
souffle::TypeAttribute
TypeAttribute
Definition: TypeAttribute.h:34
souffle::all_of
bool all_of(const Container &c, UnaryPredicate p)
A generic test checking whether all elements within a container satisfy a certain predicate.
Definition: FunctionalUtil.h:110
souffle::FunctorOp::S2F
@ S2F
souffle::FunctorOp::UMOD
@ UMOD
souffle::FunctorOp::FSUB
@ FSUB
souffle::FunctorOp::BSHIFT_R_UNSIGNED
@ BSHIFT_R_UNSIGNED
souffle::FunctorOp::F2U
@ F2U
souffle::FunctorOp::BAND
@ BAND
souffle::FunctorOp::F2S
@ F2S
souffle::FunctorOp::UBAND
@ UBAND
souffle::FunctorOp::SUB
@ SUB
souffle::FunctorOp::ULXOR
@ ULXOR
souffle::FunctorOp::UDIV
@ UDIV
souffle
Definition: AggregateOp.h:25
souffle::FunctorOp::BSHIFT_R
@ BSHIFT_R
souffle::FunctorOp::UADD
@ UADD
souffle::FunctorOp::MUL
@ MUL
FunctorOps.h
souffle::FunctorOp::SMIN
@ SMIN
souffle::isInfixFunctorOp
bool isInfixFunctorOp(std::string_view symbol)
Determines whether a functor should be written using infix notation (e.g.
Definition: FunctorOps.cpp:252
FunctionalUtil.h
souffle::FunctorOp::ADD
@ ADD
Binary Functor Operators.
souffle::FunctorOp::S2U
@ S2U
souffle::FunctorOp::SUBSTR
@ SUBSTR
Ternary Functor Operators.
OP_3
#define OP_3(op, t0, t1, t2, tDst, multi)
Definition: FunctorOps.cpp:139
souffle::FunctorOp::MAX
@ MAX
souffle::FunctorOp::FMAX
@ FMAX
souffle::FunctorOp::I2U
@ I2U
std::type
ElementType type
Definition: span.h:640
souffle::FunctorOp::MIN
@ MIN
souffle::FunctorOp::ULNOT
@ ULNOT
souffle::FUNCTOR_INTRINSIC_PREFIX_NEGATE_NAME
constexpr char FUNCTOR_INTRINSIC_PREFIX_NEGATE_NAME[]
Definition: FunctorOps.h:147
souffle::FunctorOp::LXOR
@ LXOR