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