1 | /*
|
2 | * Souffle - A Datalog Compiler
|
3 | * Copyright (c) 2020, The Souffle Developers. All rights reserved.
|
4 | * Licensed under the Universal Permissive License v 1.0 as shown at:
|
5 | * - https://opensource.org/licenses/UPL
|
6 | * - <souffle root>/licenses/SOUFFLE-UPL.txt
|
7 | */
|
8 |
|
9 | /************************************************************************
|
10 | *
|
11 | * @file EvaluatorUtils.h
|
12 | *
|
13 | * Defines utility functions used by synthesised and interpreter code.
|
14 | *
|
15 | ***********************************************************************/
|
16 |
|
17 | #pragma once
|
18 |
|
19 | #include "souffle/RamTypes.h"
|
20 | #include "souffle/utility/StringUtil.h"
|
21 | #include "souffle/utility/tinyformat.h"
|
22 | #include <csignal>
|
23 |
|
24 | namespace souffle::evaluator {
|
25 |
|
26 | template <typename A, typename F /* Tuple<RamDomain,1> -> void */>
|
27 | void runRange(const A from, const A to, const A step, F&& go) {
|
28 | #define GO(x) go(Tuple<RamDomain, 1>{ramBitCast(x)})
|
29 | if (0 < step) {
|
30 | for (auto x = from; x < to; x += step) {
|
31 | GO(x);
|
32 | }
|
33 | } else if (step < 0) {
|
34 | for (auto x = from; to < x; x += step) {
|
35 | GO(x);
|
36 | }
|
37 | } else if (from != to) {
|
38 | // `step = 0` edge case, only if non-empty range
|
39 | GO(from);
|
40 | }
|
41 | #undef GO
|
42 | }
|
43 |
|
44 | template <typename A, typename F /* Tuple<RamDomain,1> -> void */>
|
45 | void runRangeBackward(const A from, const A to, F&& func) {
|
46 | assert(from > to);
|
47 | if (from > to) {
|
48 | for (auto x = from; x > to; --x) {
|
49 | func(Tuple<RamDomain, 1>{ramBitCast(x)});
|
50 | }
|
51 | }
|
52 | }
|
53 |
|
54 | template <typename A, typename F /* Tuple<RamDomain,1> -> void */>
|
55 | void runRange(const A from, const A to, F&& go) {
|
56 | if constexpr (std::is_unsigned<A>()) {
|
57 | if (from <= to) {
|
58 | runRange(from, to, static_cast<A>(1U), std::forward<F>(go));
|
59 | } else {
|
60 | runRangeBackward(from, to, std::forward<F>(go));
|
61 | }
|
62 | } else {
|
63 | return runRange(from, to, A(from <= to ? 1 : -1), std::forward<F>(go));
|
64 | }
|
65 | }
|
66 |
|
67 | template <typename A>
|
68 | A symbol2numeric(const std::string& src) {
|
69 | try {
|
70 | if constexpr (std::is_same_v<RamFloat, A>) {
|
71 | return RamFloatFromString(src);
|
72 | } else if constexpr (std::is_same_v<RamSigned, A>) {
|
73 | return RamSignedFromString(src, nullptr, 0);
|
74 | } else if constexpr (std::is_same_v<RamUnsigned, A>) {
|
75 | return RamUnsignedFromString(src, nullptr, 0);
|
76 | } else {
|
77 | static_assert(sizeof(A) == 0, "Invalid type specified for symbol2Numeric");
|
78 | }
|
79 |
|
80 | } catch (...) {
|
81 | tfm::format(std::cerr, "error: wrong string provided by `to_number(\"%s\")` functor.\n", src);
|
82 | raise(SIGFPE);
|
83 | abort(); // UNREACHABLE: `raise` lacks a no-return attribute
|
84 | }
|
85 | };
|
86 |
|
87 | template <typename A>
|
88 | bool lxor(A x, A y) {
|
89 | return (x || y) && (!x != !y);
|
90 | }
|
91 |
|
92 | // HACK: C++ doesn't have an infix logical xor operator.
|
93 | // C++ doesn't allow defining new infix operators.
|
94 | // C++ isn't a very nice language, but C++ does allow overload-based war crimes.
|
95 | // This particular war crime allows a very verbose infix op for `lxor`.
|
96 | // It should only be used in macro dispatches.
|
97 | struct lxor_infix {
|
98 | template <typename A>
|
99 | struct curry {
|
100 | A x;
|
101 | bool operator+(A y) const {
|
102 | return lxor(x, y);
|
103 | }
|
104 | };
|
105 | };
|
106 |
|
107 | template <typename A>
|
108 | lxor_infix::curry<A> operator+(A x, lxor_infix) {
|
109 | return lxor_infix::curry<A>{x};
|
110 | }
|
111 |
|
112 | } // namespace souffle::evaluator
|