| 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
 |