diff --git a/main.c b/main.c index 04522b9..ed9464a 100644 --- a/main.c +++ b/main.c @@ -21,13 +21,13 @@ const char* command = NULL; struct { bool not; - + bool or, orn, nor; - + bool and, andn, nand; - + bool xor, nxor; - + bool lt, lte, gt, gte; bool ternary; @@ -37,32 +37,36 @@ bool assume_yes = false; bool verbose = false; +bool print_with_color = false; + static void parse_args(int argc, char* const* argv) { bool unset_operators = true; - - for (int opt; (opt = getopt(argc, argv, "pyvc:eEo:")) != -1; ) switch (opt) + + print_with_color = isatty(1); + + for (int opt; (opt = getopt(argc, argv, "pyvc:eEo:C:")) != -1; ) switch (opt) { case 'p': print_all_and_quit = true; break; - + case 'y': assume_yes = true; break; - + case 'v': verbose = true; break; - + case 'c': command = optarg; break; - + case 'E': use_operators.ternary = true; // fallthrough - + case 'e': { use_operators.not = true; @@ -71,13 +75,13 @@ static void parse_args(int argc, char* const* argv) use_operators.orn = true; use_operators.nor = true; - + use_operators.andn = true; use_operators.nand = true; - + use_operators. xor = true; use_operators.nxor = true; - + unset_operators = false; break; } @@ -85,7 +89,7 @@ static void parse_args(int argc, char* const* argv) case 'o': { unset_operators = false; - + for (char* moving; (moving = strtok_r(NULL, ",", &optarg)); ) { if (!strcmp(moving, "not") || !strcmp(moving, "!")) @@ -123,12 +127,28 @@ static void parse_args(int argc, char* const* argv) } break; } - + + case 'C': + { + if (!strcmp(optarg, "yes") || !strcmp(optarg, "on")) + print_with_color = true; + else if (!strcmp(optarg, "no") || !strcmp(optarg, "off")) + print_with_color = false; + else if (!strcmp(optarg, "auto")) + print_with_color = isatty(1); + else + { + assert(!"TODO"); + } + + break; + } + default: assert(!"TODO"); break; } - + if (unset_operators) { use_operators.not = true; @@ -153,20 +173,20 @@ enum kind { ek_X, ek_Y, ek_Z, - + ek_not, ek_or, ek_and, - + ek_orn, ek_nor, - + ek_andn, ek_nand, - + ek_xor, ek_nxor, - + ek_lt, ek_lte, ek_gt, @@ -177,87 +197,152 @@ enum kind { struct expr { enum kind kind; - + uint16_t cond, left, right; } lookup[N]; // truthtable -> cost int costs[N] = {}; -static void print(uint16_t truthtable) +static void print(uint16_t truthtable, int depth) { + #define LITERAL_ESCAPE "\e[38;2;200;200;100m" + #define VARIABLE_ESCAPE "\e[38;2;100;100;200m" + #define RESET_ESCAPE "\e[0m" + + static const char* const operator_colors[10] = { + "\e[38;2;204;0;0m", + "\e[38;2;204;122;0m", + "\e[38;2;163;204;0m", + "\e[38;2;40;204;0m", + "\e[38;2;0;204;81m", + "\e[38;2;0;204;204m", + "\e[38;2;0;81;204m", + "\e[38;2;40;0;204m", + "\e[38;2;163;0;204m", + "\e[38;2;204;0;122m", + }; + const struct expr* e = &lookup[truthtable]; - + switch (e->kind) { case ek_undef: assert(!"NOPE"); break; - - case ek_0: printf("0"); break; - case ek_1: printf("1"); break; - - case ek_W: printf("w"); break; - case ek_X: printf("x"); break; - case ek_Y: printf("y"); break; - case ek_Z: printf("z"); break; - + + case ek_0: + { + printf(print_with_color ? LITERAL_ESCAPE "0" RESET_ESCAPE : "0"); + break; + } + + case ek_1: + { + printf(print_with_color ? LITERAL_ESCAPE "1" RESET_ESCAPE : "1"); + break; + } + + case ek_W: + { + printf(print_with_color ? VARIABLE_ESCAPE "w" RESET_ESCAPE : "w"); + break; + } + + case ek_X: + { + printf(print_with_color ? VARIABLE_ESCAPE "x" RESET_ESCAPE : "x"); + break; + } + + case ek_Y: + { + printf(print_with_color ? VARIABLE_ESCAPE "y" RESET_ESCAPE : "y"); + break; + } + + case ek_Z: + { + printf(print_with_color ? VARIABLE_ESCAPE "z" RESET_ESCAPE : "z"); + break; + } + case ek_not: - printf("(!"), print(e->left), printf(")"); - break; - - case ek_or: - printf("("), print(e->left), printf(" || "), print(e->right), printf(")"); - break; - - case ek_orn: - printf("("), print(e->left), printf(" |! "), print(e->right), printf(")"); - break; - - case ek_nor: - printf("("), print(e->left), printf(" !| "), print(e->right), printf(")"); - break; - - case ek_and: - printf("("), print(e->left), printf(" && "), print(e->right), printf(")"); - break; - - case ek_andn: - printf("("), print(e->left), printf(" &! "), print(e->right), printf(")"); - break; - - case ek_nand: - printf("("), print(e->left), printf(" !& "), print(e->right), printf(")"); - break; - - case ek_xor: - printf("("), print(e->left), printf(" != "), print(e->right), printf(")"); - break; - - case ek_nxor: - printf("("), print(e->left), printf(" == "), print(e->right), printf(")"); - break; - - case ek_lt: - printf("("), print(e->left), printf(" < "), print(e->right), printf(")"); - break; + { + const char* start = print_with_color ? operator_colors[depth % 10] : ""; + const char* end = print_with_color ? RESET_ESCAPE : ""; - case ek_lte: - printf("("), print(e->left), printf(" <= "), print(e->right), printf(")"); - break; + printf("%s(!%s", start, end); - case ek_gt: - printf("("), print(e->left), printf(" > "), print(e->right), printf(")"); - break; + print(e->left, depth + 1); - case ek_gte: - printf("("), print(e->left), printf(" >= "), print(e->right), printf(")"); + printf("%s)%s", start, end); break; + } + + #define BINARY_OPERATOR(kind, operatorstring) \ + case kind: \ + { \ + const char *start = "", *end = ""; \ + \ + if (print_with_color) \ + { \ + start = operator_colors[depth % 10]; \ + \ + end = RESET_ESCAPE; \ + } \ + \ + printf("%s(%s", start, end); \ + \ + print(e->left, depth + 1); \ + \ + printf("%s%s%s", start, operatorstring, end); \ + \ + print(e->right, depth + 1); \ + \ + printf("%s)%s", start, end); \ + break; \ + } + + BINARY_OPERATOR(ek_or, " || "); + BINARY_OPERATOR(ek_orn, " |! "); + BINARY_OPERATOR(ek_nor, " !| "); + + BINARY_OPERATOR(ek_and, " && "); + BINARY_OPERATOR(ek_andn, " &! "); + BINARY_OPERATOR(ek_nand, " !& "); + + BINARY_OPERATOR(ek_xor, " != "); + BINARY_OPERATOR(ek_nxor, " == "); + + BINARY_OPERATOR(ek_lt, " < "); + BINARY_OPERATOR(ek_lte, " <= "); + BINARY_OPERATOR(ek_gt, " > "); + BINARY_OPERATOR(ek_gte, " >= "); + + #undef BINARY_OPERATOR case ek_ternary: - printf("("), print(e->cond), printf(" ? "), - print(e->left), printf(" : "), print(e->right), printf(")"); + { + const char *start = "", *end = ""; + + if (print_with_color) + { + start = operator_colors[depth % 10]; + + end = RESET_ESCAPE; + } + + printf("%s(%s", start, end); + print(e->cond, depth + 1); + printf(" %s?%s ", start, end); + print(e->left, depth + 1); + printf(" %s:%s ", start, end); + print(e->right, depth + 1); + printf("%s)%s", start, end); + break; + } } } @@ -266,112 +351,112 @@ void calculate_simplifications(void) // heap of truthtables; key = cost uint16_t todo[N]; int todo_n = 0; - + // truthtable -> index in 'todo' int reverse[N]; - + // init: for (int i = 0; i < N; i++) { lookup[i].kind = ek_undef; - + reverse[i] = -1; - + costs[i] = INT_MAX; } uint16_t pop(void) { assert(todo_n > 0); - + uint16_t retval = todo[0]; - + reverse[retval] = -1; - + uint16_t moving = todo[todo_n-- - 1]; - + int cost = costs[moving]; - + int index = 0; - + again: { int left = index * 2 + 1; int right = index * 2 + 2; - + uint16_t smallest = moving; - + if (left < todo_n && costs[todo[left]] < cost) smallest = todo[left]; - + if (right < todo_n && costs[todo[right]] < costs[smallest]) smallest = todo[right]; - + if (smallest == moving) { todo[index] = moving; - + reverse[moving] = index; } else { int new = reverse[smallest]; - + todo[index] = smallest; - + reverse[smallest] = index; - + index = new; - + goto again; } } - + return retval; } void append(uint16_t truthtable, int cost) { assert(reverse[truthtable] == -1); - + int index = todo_n++, new_index; - + while (index > 0 && costs[todo[new_index = (index - 1) / 2]] > cost) { todo[index] = todo[new_index]; - + reverse[todo[new_index]] = index; - + index = new_index; } - + todo[index] = truthtable; - + reverse[truthtable] = index; - + costs[truthtable] = cost; } void update(uint16_t truthtable, int cost) { assert(reverse[truthtable] != -1); - + assert(cost < costs[truthtable]); - + int index = reverse[truthtable], new_index; - + while (index > 0 && costs[todo[new_index = (index - 1) / 2]] > cost) { todo[index] = todo[new_index]; - + reverse[todo[new_index]] = index; - + index = new_index; } - + todo[index] = truthtable; reverse[truthtable] = index; - + costs[truthtable] = cost; } @@ -379,32 +464,32 @@ void calculate_simplifications(void) append(X, 0), lookup[X].kind = ek_X; append(Y, 0), lookup[Y].kind = ek_Y; append(Z, 0), lookup[Z].kind = ek_Z; - + append(0, 1), lookup[0].kind = ek_0; append(M, 1), lookup[M].kind = ek_1; - + while (todo_n) { uint16_t truthtable = pop(); - + int cost = costs[truthtable]; - + if (verbose) { int left = N - todo_n; - + printf("%i of %i (%.2f%%): [%i] ", left, N, (100.0 * left / N), cost); - - print(truthtable), puts(""); + + print(truthtable, 0), puts(""); } - + // consider NOT: if (use_operators.not) { uint16_t not_truthtable = ~truthtable & M; - + int not_cost = cost + 1; - + if (not_cost < costs[not_truthtable]) { if (reverse[not_truthtable] == -1) @@ -415,12 +500,12 @@ void calculate_simplifications(void) { update(not_truthtable, not_cost); } - + lookup[not_truthtable].kind = ek_not; lookup[not_truthtable].left = truthtable; } } - + #define BINARY_OPERATOR(ekind, function) \ { \ for (int i = 0; i < N; i++) \ @@ -465,17 +550,17 @@ void calculate_simplifications(void) } \ } \ } - + #define OR(a, b) ((a) | (b)) #define AND(a, b) ((a) & (b)) - + #define ORN(a, b) (~(a) | (b)) #define NOR(a, b) ~( (a) | (b)) #define ANDN(a, b) (~(a) & (b)) #define NAND(a, b) ~( (a) & (b)) #define XOR(a, b) (( a) ^ (b)) #define NXOR(a, b) ~( (a) ^ (b)) - + #define LT(a, b) ((~a) & (b)) #define LTE(a, b) ((~a) | (b)) #define GT(a, b) (( a) & ~(b)) @@ -485,42 +570,42 @@ void calculate_simplifications(void) { BINARY_OPERATOR(ek_or, OR); } - + if (use_operators.and) { BINARY_OPERATOR(ek_and, AND); } - + if (use_operators.orn) { BINARY_OPERATOR(ek_orn, ORN); } - + if (use_operators.nor) { BINARY_OPERATOR(ek_nor, NOR); } - + if (use_operators.andn) { BINARY_OPERATOR(ek_andn, ANDN); } - + if (use_operators.nand) { BINARY_OPERATOR(ek_nand, NAND); } - + if (use_operators.xor) { BINARY_OPERATOR(ek_xor, XOR); } - + if (use_operators.nxor) { BINARY_OPERATOR(ek_nxor, NXOR); } - + if (use_operators.lt) { BINARY_OPERATOR(ek_lt, LT); @@ -552,7 +637,7 @@ void calculate_simplifications(void) if (costs[j] != INT_MAX) { int ternary_cost = 1 + cost + costs[i] + costs[j]; - + #define TERNARY(C, T, F) \ { \ uint16_t ternary_truthtable = \ @@ -575,7 +660,7 @@ void calculate_simplifications(void) lookup[ternary_truthtable].right = (F); \ } \ } \ - + TERNARY(truthtable, i, j); TERNARY(i, truthtable, j); TERNARY(i, j, truthtable); @@ -590,34 +675,34 @@ void calculate_simplifications(void) void get_simplifications(void) { char path[PATH_MAX] = ".simplifier-cache"; - + if (use_operators.not) strcat(path, "-not"); - + if (use_operators.or) strcat(path, "-or"); - + if (use_operators.orn) strcat(path, "-orn"); - + if (use_operators.nor) strcat(path, "-nor"); - + if (use_operators.and) strcat(path, "-and"); - + if (use_operators.andn) strcat(path, "-andn"); - + if (use_operators.nand) strcat(path, "-nand"); - + if (use_operators.xor) strcat(path, "-xor"); - + if (use_operators.nxor) strcat(path, "-nxor"); - + if (use_operators.lt) strcat(path, "-lt"); @@ -632,21 +717,21 @@ void get_simplifications(void) if (use_operators.ternary) strcat(path, "-ternary"); - + strcat(path, ".bin"); - + int fd = open(path, O_RDONLY); - + if (fd > 0) { // great, just read it into memory - + if (read(fd, lookup, sizeof(lookup)) < (ssize_t) sizeof(lookup)) { printf("%s: read() to cache failed: %m\n", argv0); exit(1); } - + if (read(fd, costs, sizeof(costs)) < (ssize_t) sizeof(costs)) { printf("%s: read() to cache failed: %m\n", argv0); @@ -662,29 +747,29 @@ void get_simplifications(void) "\n" "This may take a while." "\n" ""); - + if (!verbose) { puts("re-run with '-v' to watch progress"); } - + if (!assume_yes) { puts(""); puts("any input to start:"), getchar(); puts(""); } - + calculate_simplifications(); - + fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0664); - + if (write(fd, lookup, sizeof(lookup)) < (ssize_t) sizeof(lookup)) { printf("%s: write() to cache failed: %m\n", argv0); exit(1); } - + if (write(fd, costs, sizeof(costs)) < (ssize_t) sizeof(costs)) { printf("%s: write() to cache failed: %m\n", argv0); @@ -696,7 +781,7 @@ void get_simplifications(void) // something else assert(!"TODO"); } - + if (fd > 0) { close(fd); @@ -707,112 +792,112 @@ uint16_t evaluate(const char* text) { enum { tk_uninitialized, - + tk_0, tk_1, - + tk_w, tk_x, tk_y, tk_z, - + tk_oparen, tk_cparen, - + tk_emark, tk_emarkequals, tk_emarkvbar, tk_emarkampersand, - + tk_equalsequals, - + tk_vbarvbar, tk_vbaremark, - + tk_qmark, - + tk_less_than, tk_less_than_eq, - + tk_greater_than, tk_greater_than_eq, - + tk_ampersandemark, tk_ampersandampersand, - + tk_colon, - + tk_EOF, } tokenkind = tk_uninitialized; - + const char* moving = text; - + void next_token(void) { while (*moving && *moving == ' ') moving++; - + switch (*moving) { case 0: tokenkind = tk_EOF; break; - + case '0': tokenkind = tk_0, moving++; break; - + case '1': tokenkind = tk_1, moving++; break; - + case 'w': tokenkind = tk_w, moving++; break; - + case 'x': tokenkind = tk_x, moving++; break; - + case 'y': tokenkind = tk_y, moving++; break; - + case 'z': tokenkind = tk_z, moving++; break; - + case '(': tokenkind = tk_oparen, moving++; break; - + case ')': tokenkind = tk_cparen, moving++; break; - + case '?': tokenkind = tk_qmark, moving++; break; - + case ':': tokenkind = tk_colon, moving++; break; - + // either '||' or '|!': case '|': { moving++; - + switch (*moving) { case '|': tokenkind = tk_vbarvbar, moving++; break; - + case '!': tokenkind = tk_vbaremark, moving++; break; - + default: { puts("syntax error"); @@ -820,25 +905,25 @@ uint16_t evaluate(const char* text) break; } } - + break; } - + // either '&&' or '&!': case '&': { moving++; - + switch (*moving) { case '&': tokenkind = tk_ampersandampersand, moving++; break; - + case '!': tokenkind = tk_ampersandemark, moving++; break; - + default: { puts("syntax error"); @@ -846,88 +931,88 @@ uint16_t evaluate(const char* text) break; } } - + break; } - + // either '!' or '!=' or '!&' or '!|' case '!': { moving++; - + switch (*moving) { case '=': tokenkind = tk_emarkequals, moving++; break; - + case '|': tokenkind = tk_emarkvbar, moving++; break; - + case '&': tokenkind = tk_emarkampersand, moving++; break; - + default: tokenkind = tk_emark; break; } - + break; } - + case '<': { moving++; - + switch (*moving) { case '=': tokenkind = tk_less_than_eq, moving++; break; - + default: { tokenkind = tk_less_than; break; } } - + break; } - + case '>': { moving++; - + switch (*moving) { case '=': tokenkind = tk_greater_than_eq, moving++; break; - + default: { tokenkind = tk_greater_than; break; } } - + break; } - + // could only be '==': case '=': { moving++; - + switch (*moving) { case '=': tokenkind = tk_equalsequals, moving++; break; - + default: { puts("syntax error"); @@ -935,18 +1020,18 @@ uint16_t evaluate(const char* text) break; } } - + break; } - + default: assert(!"TODO"); break; } } - + next_token(); - + uint16_t parse_root(void) { uint16_t parse_ternary(void) @@ -964,49 +1049,49 @@ uint16_t evaluate(const char* text) uint16_t parse_primary(void) { uint16_t retval; - + switch (tokenkind) { case tk_0: retval = 0, next_token(); break; - + case tk_1: retval = M, next_token(); break; - + case tk_w: retval = W, next_token(); break; - + case tk_x: retval = X, next_token(); break; - + case tk_y: retval = Y, next_token(); break; - + case tk_z: retval = Z, next_token(); break; - + case tk_oparen: assert(!"TODO"); break; - + default: assert(!"TODO"); break; } - + return retval; } - + if (tokenkind == tk_emark) { next_token(); - + return ~parse_prefix(); } else @@ -1014,9 +1099,9 @@ uint16_t evaluate(const char* text) return parse_primary(); } } - + uint16_t left = parse_prefix(); - + again: switch (tokenkind) { case tk_less_than: @@ -1025,37 +1110,37 @@ uint16_t evaluate(const char* text) left = (~left & parse_equals()) & M; goto again; } - + case tk_less_than_eq: { next_token(); left = (~left | parse_equals()) & M; goto again; } - + case tk_greater_than_eq: { next_token(); left = ( left | ~parse_equals()) & M; goto again; } - + case tk_greater_than: { next_token(); left = ( left & ~parse_equals()) & M; goto again; } - + default: break; } - + return left; } - + uint16_t left = parse_compares(); - + again: switch (tokenkind) { case tk_equalsequals: @@ -1064,23 +1149,23 @@ uint16_t evaluate(const char* text) left = ~(left ^ parse_equals()) & M; goto again; } - + case tk_emarkequals: { next_token(); left = left ^ parse_equals(); goto again; } - + default: break; } - + return left; } - + uint16_t left = parse_equals(); - + again: switch (tokenkind) { case tk_ampersandampersand: @@ -1089,30 +1174,30 @@ uint16_t evaluate(const char* text) left = left & parse_equals(); goto again; } - + case tk_ampersandemark: { next_token(); left = ~left & parse_equals(); goto again; } - + case tk_emarkampersand: { next_token(); left = ~(left & parse_equals()); goto again; } - + default: break; } - + return left; } - + uint16_t left = parse_ands(); - + again: switch (tokenkind) { case tk_vbarvbar: @@ -1121,46 +1206,46 @@ uint16_t evaluate(const char* text) left = left | parse_ands(); goto again; } - + case tk_vbaremark: { next_token(); left = (~left | parse_ands()) & M; goto again; } - + case tk_emarkvbar: { next_token(); left = ~(left | parse_ands()) & M; goto again; } - + default: break; } - + return left; } - + uint16_t cond = parse_ors(); - + if (tokenkind == tk_qmark) { next_token(); - + uint16_t left = parse_ors(); - + if (tokenkind != tk_colon) { puts("syntax error!"); exit(1); } - + next_token(); - + uint16_t right = parse_ternary(); - + return (cond & left) | (~cond & right); } else @@ -1168,52 +1253,52 @@ uint16_t evaluate(const char* text) return cond; } } - + return parse_ternary(); } - + uint16_t truthtable = parse_root(); - + if (tokenkind != tk_EOF) { puts("syntax error!"); exit(1); } - + return truthtable; } int main(int argc, char* const* argv) { parse_args(argc, argv); - + get_simplifications(); - + if (print_all_and_quit) { for (int i = 0; i < N; i++) { int cost = costs[i]; - + if (cost != INT_MAX) { - printf("0b%016b: [%2i]: ", i, cost), print(i), puts(""); + printf("0b%016b: [%2i]: ", i, cost), print(i, 0), puts(""); } } } else if (command) { uint16_t truthtable = evaluate(command); - + // printf("truthtable = 0b%016b\n", truthtable); - + if (costs[truthtable] == INT_MAX) { puts("unreachable"); } else { - printf("%2i: ", costs[truthtable]), print(truthtable), puts(""); + printf("%2i: ", costs[truthtable]), print(truthtable, 0), puts(""); } } else @@ -1230,7 +1315,7 @@ int main(int argc, char* const* argv) } } - // we subtract 4 from the cost because + // we subtract 4 from the cost because puts(""); printf("I can simplify any tree down to %i operators or less.\n", max_cost); @@ -1239,16 +1324,16 @@ int main(int argc, char* const* argv) for (char* line; (line = readline(">>> ")); free(line)) { uint16_t truthtable = evaluate(line); - + // printf("truthtable = 0b%016b\n", truthtable); - + if (costs[truthtable] == INT_MAX) { puts("unreachable"); } else { - printf("%2i: ", costs[truthtable]), print(truthtable), puts(""); + printf("%2i: ", costs[truthtable]), print(truthtable, 0), puts(""); } } } diff --git a/makefile b/makefile index cb1deed..4234f8a 100644 --- a/makefile +++ b/makefile @@ -7,7 +7,7 @@ cc = gcc cppflags = -D _GNU_SOURCE -cflags = -Werror -Wall -Wextra -Wstrict-prototypes +cflags = -Werror -Wall -Wextra -Wstrict-prototypes -Wfatal-errors cflags += -O3 @@ -20,3 +20,7 @@ ldflags += -lreadline run: /tmp/4-variable-simplifier $< $(args) + + +# nix --extra-experimental-features nix-command --extra-experimental-features flakes develop --command 'make' +