diff --git a/src/util/lut.c b/src/util/lut.c index 336b4e90cae..560353e5b89 100644 --- a/src/util/lut.c +++ b/src/util/lut.c @@ -3,6 +3,10 @@ * SPDX-License-Identifier: MIT */ +#include "lut.h" + +#include + /* * This table was generated by an offline tool in the following manner: * for each function value, we generated a tree of all expressions that evaluate @@ -269,3 +273,124 @@ const char *util_lut3_to_str[256] = { [0xFE] = "a | b | c", [0xFF] = "ones", }; + +#if !defined(_MSC_VER) +struct lut3_parser { + const char *s; + bool error; +}; + +static void +lut3_skip_space(struct lut3_parser *p) +{ + while (p->s[0] == ' ') + p->s++; +} + +static bool +lut3_match(struct lut3_parser *p, char c) +{ + lut3_skip_space(p); + + if (p->s[0] == c) { + p->s++; + return true; + } else { + return false; + } +} + +static bool +lut3_match_str(struct lut3_parser *p, const char *pat) +{ + lut3_skip_space(p); + + size_t len = strlen(pat); + if (!strncmp(p->s, pat, len)) { + p->s += len; + return true; + } else { + return false; + } +} + +static util_lut3 lut3_parse_or(struct lut3_parser *p); + +static util_lut3 +lut3_parse_value(struct lut3_parser *p) +{ + if (lut3_match(p, 'a')) return UTIL_LUT3(a); + if (lut3_match(p, 'b')) return UTIL_LUT3(b); + if (lut3_match(p, 'c')) return UTIL_LUT3(c); + + if (lut3_match_str(p, "zeros")) return UTIL_LUT3(0); + if (lut3_match_str(p, "ones")) return UTIL_LUT3(~0); + + if (lut3_match(p, '(')) { + util_lut3 val = lut3_parse_or(p); + if (lut3_match(p, ')')) + return val; + } + + p->error = true; + return 0; +} + +static util_lut3 +lut3_parse_not(struct lut3_parser *p) +{ + bool has_not = false; + while (lut3_match(p, '~')) + has_not = !has_not; + util_lut3 val = lut3_parse_value(p); + return has_not ? ~val : val; +} + +static util_lut3 +lut3_parse_and(struct lut3_parser *p) +{ + util_lut3 val = lut3_parse_not(p); + while (lut3_match(p, '&')) + val &= lut3_parse_not(p); + return val; +} + +static util_lut3 +lut3_parse_xor(struct lut3_parser *p) +{ + util_lut3 val = lut3_parse_and(p); + while (lut3_match(p, '^')) + val ^= lut3_parse_and(p); + return val; +} + +static util_lut3 +lut3_parse_or(struct lut3_parser *p) +{ + util_lut3 val = lut3_parse_xor(p); + while (lut3_match(p, '|')) + val |= lut3_parse_xor(p); + return val; +} + +util_lut3 +util_lut3_parse(const char *s, bool *ok) +{ + struct lut3_parser p = { .s = s }; + + util_lut3 val = 0; + + if (s && s[0]) { + val = lut3_parse_or(&p); + lut3_skip_space(&p); + if (p.s[0]) + p.error = true; + } else { + p.error = true; + } + + if (ok) + *ok = !p.error; + return val; +} +#endif diff --git a/src/util/lut.h b/src/util/lut.h index 0e9965e7a1f..335f79a3205 100644 --- a/src/util/lut.h +++ b/src/util/lut.h @@ -6,9 +6,14 @@ #pragma once #include +#include #include #include "util/macros.h" +#ifdef __cplusplus +extern "C" { +#endif + /* * Represents a boolean lookup table in sum-of-minterms form. These are * natural encodings, matching the Intel BFN and Apple BITOP instructions. @@ -61,6 +66,8 @@ util_lut2_invert_source(util_lut2 l, unsigned s) { return (util_lut2)(util_lut3_invert_source((util_lut3)l, s) & 0xf); } + +util_lut3 util_lut3_parse(const char *s, bool *ok); #endif /* @@ -102,6 +109,10 @@ util_lut3_swap_sources(util_lut3 l, unsigned a, unsigned b) UNREACHABLE("invalid source selection"); } - /* Finding minimal string forms of LUTs is tricky, so we precompute. */ extern const char *util_lut3_to_str[256]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + diff --git a/src/util/tests/lut_test.cpp b/src/util/tests/lut_test.cpp index 8ee882799e8..846687ac94b 100644 --- a/src/util/tests/lut_test.cpp +++ b/src/util/tests/lut_test.cpp @@ -72,4 +72,55 @@ TEST(lut, swap_sources3) EXPECT_LUT3(util_lut3_swap_sources(UTIL_LUT3(a | ~b | c), 0, 2), "a | ~b | c"); EXPECT_LUT3(util_lut3_swap_sources(UTIL_LUT3(a | ~b | c), 1, 2), "a | b | ~c"); } + +TEST(lut, parse) +{ + for (unsigned i = 0; i < ARRAY_SIZE(util_lut3_to_str); i++) { + const char *str = util_lut3_to_str[i]; + bool ok = false; + const unsigned got = util_lut3_parse(str, &ok); + EXPECT_TRUE(ok) << "CASE: " << str; + EXPECT_EQ(got, i) << "CASE: " << str; + } + + const char *invalids[] = { + NULL, + "", + " ", + "a)", + "onesx", + "a &", + "((a)", + }; + + for (unsigned i = 0; i < ARRAY_SIZE(invalids); i++) { + bool ok = true; + util_lut3_parse(invalids[i], &ok); + EXPECT_FALSE(ok) << "CASE #" << i; + } + + struct { + const char *a; + const char *b; + } equivalents[] = { + { "~~a", "a" }, + { "a | b & c", "a | (b & c)" }, + { "a | b ^ c", "a | (b ^ c)" }, + { "a & (b | c)", "a & b | a & c" }, + { "a & ones", "a" }, + { "~(a & b)", "~a | ~b" }, + { "((a | b) & (b | c))", "(a | b) & (b | c)" }, + }; + + for (unsigned i = 0; i < ARRAY_SIZE(equivalents); i++) { + bool ok_a = false; + bool ok_b = false; + util_lut3 a = util_lut3_parse(equivalents[i].a, &ok_a); + util_lut3 b = util_lut3_parse(equivalents[i].b, &ok_b); + EXPECT_TRUE(ok_a) << equivalents[i].a; + EXPECT_TRUE(ok_b) << equivalents[i].b; + EXPECT_EQ(a, b) << equivalents[i].a << " and " << equivalents[i].b; + } +} + #endif