
/* test program for C programming language operators,
 * namely the binary operators which only involve rvalues,
 * and are applicable on ints
 */

/* note: the generated code isn't intended to test for compiler bugs
 * (in a specific compiling environment);
 * namely the generated code isn't split into code-to-be-tested
 * (e.g. to be tried with different optimization flags),
 * and reference-code (to be compiled conservatively), and driver code
 */

/* compiling the generated code may provoke compiler warnings
 * due to unusual or nonsensical expressions, e.g.:
 * "suggest parentheses around + or - inside shift"
 * "suggest parentheses around + or - in operand of &"
 * "suggest parentheses around arithmetic in operand of ^"
 * "suggest parentheses around arithmetic in operand of |"
 * "comparisons like X<=Y<=Z do not have their mathematical meaning"
 * "suggest parentheses around comparison in operand of &"
 * "suggest parentheses around comparison in operand of ^"
 * "suggest parentheses around comparison in operand of |"
 * "suggest parentheses around && within ||"
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void iterops(void (*f)(char const* op, int prec, int num, void* data),
	void* data);
static void dumpop(char const* op, int prec, int num, void* data);
static void genoppairs(void (*f)(char const* op1, int prec1, int num1,
	char const* op2, int prec2, int num2, void* data), void* data);
static void genoppairs2(char const* op, int prec, int num, void* data);
static void genoppairs3(char const* op, int prec, int num, void* data);
static void gentest(char const* op1, int prec1, int num1,
	char const* op2, int prec2, int num2, void* data);
static void genname(char const* op1, int prec1, int num1,
	char const* op2, int prec2, int num2, void* data);
static void genname2(char const* op1, int prec1, int num1,
	char const* op2, int prec2, int num2, void* data);
static void genfunclist(char const* name, FILE* out);
static void genperms3(int a, int b, int c);
static void genperms2(int a, int b);

/* from C operator precedences table */
static char const* const mult[] = {"*", "/", "%", NULL};
static char const* const plus[] = {"+", "-", NULL};
static char const* const shft[] = {"<<", ">>", NULL};
static char const* const cmpr[] = {"<", "<=", ">", ">=", NULL};
static char const* const eqls[] = {"==", "!=", NULL};
static char const* const aand[] = {"&", NULL};
static char const* const axor[] = {"^", NULL};
static char const* const aror[] = {"|", NULL};
static char const* const land[] = {"&&", NULL};
static char const* const loor[] = {"||", NULL};
static char const* const* const precs[] = {
	mult, plus, shft, cmpr, eqls, aand, axor, aror, land, loor, NULL};

static int dumptable = 0;
static int verbose = 0;
static int more = 0;
static int status = 0;

int main(int argc, char** argv) {
	int i, n, j;

	for (i = 1; i < argc; i++) {
		if (strcmp(argv[i], "-v") == 0) {
			verbose++;
		} else if (strcmp(argv[i], "-t") == 0) {
			dumptable++;
		} else if (strcmp(argv[i], "-m") == 0) {
			more++;
		} else if (strcmp(argv[i], "-s") == 0) {
			status++;
		} else {
			fprintf(stderr, "%s: %s, %s, %s\n", argv[0],
				"-m(ore, e.g. permutations; can be repeated)",
				"-s(tatus always)",
				"-v(erbose)");
			return 3;
		}
	}

	if (dumptable) {
		iterops(dumpop, stdout);
		printf("\n");
		return 0;
	}

	printf("/");
	printf("* operator precedences tester ; generated code *");
	printf("/\n");
	printf("\n");
	printf("#include <stdio.h>\n");
	printf("\n");
	printf("enum {OK = 0, SKIP, SAME, ILLG, WRNG, ASSC};\n");
	printf("static char const* const warns[] = {\n");
	printf("\t\"ok\", \"skip\", \"same\", \"illg\", \"wrng\", \"assc\"};\n");
	printf("\n");
	genoppairs(gentest, stdout);
	genfunclist("funcs", stdout);
	printf("\n");

	printf("static int runfuncs(int a, int b, int c) {\n");
	printf("\tint i, r, n, p;\n");
	printf("\tn = 0;\n");
	printf("\tfor (i = 0; funcs[i] != NULL; i++) {\n");
	printf("\t\tp = 0;\n");
	printf("\t\tr = ((*funcs[i])(a, b, c));\n");
	printf("\t\tif (r != 0) {\n");
	if (verbose) {
		printf("\t\t\tp++;\n");
	}
	printf("\t\t\tif ((r != SKIP) && (r != SAME)) {\n");
	printf("\t\t\t\tp++;\n");
	printf("\t\t\t\tn++;\n");
	printf("\t\t\t}\n");
	printf("\t\t}\n");
	if (status) {
		printf("\t\tp++;\n");
	}
	printf("\t\tif (p) {\n");
	printf("\t\t\tprintf(\"%%s\\n\", (warns[r]));\n");
	printf("\t\t}\n");
	printf("\t}\n");
	printf("\treturn n;\n");
	printf("}\n");
	printf("\n");

	printf("int main(void) {\n");
	printf("\tint n;\n");
	printf("\tn = 0;\n");

	/* numbers chosen so that most tests don't have status 'skip' or 'same' */
	genperms3(14, 5, 3);

	if (more-- > 0) {
		/* permutations with only two (considers '==') */
		genperms2(14, 5);
	}
	if (more-- > 0) {
		/* {0,1,-1}, with permutations
		 * (considers results of boolean operations
		 * to be combined arithmetically)
		 */
		genperms3(0, 1, -1);
		genperms2(0, 1);
	}
	if (more-- > 0) {
		genperms2(0, -1);
		genperms2(1, -1);
		genperms2(1, -2);
		genperms2(2, -2);
	}
	if (more-- > 0) {
		/* including 1 == 2 / 2 case */
		genperms2(1, 2);
		genperms3(0, 1, 2);
		genperms3(1, 2, 4);
		genperms3(2, 4, 8);
		genperms3(4, 16, 64);
		genperms3(3, 5, 9);
		genperms3(3, 6, 12);
		genperms3(1, 3, 7);
	}

	/* note: some of the operator combinations are symmetric
	 * and will at most produce a 'same' status:
	 * * *, + +, & &, ^ ^, | |, && &&, || ||;
	 * some are associative enough for the same effect:
	 * + - (but not - +), * << (but not << *);
	 * slightly more complex is: == /;
	 * another case with no non-trivial solutions is: % ==;
	 */

	/* [grow exponantally] */
	for (n = 16; more-- > 0; n <<= 1) {
		for (j = n; j > 0; j--) {
			/* random numbers, with permutations */
			genperms3(rand(), rand(), rand());
		}
		n <<= 1;
	}

	printf("\treturn n;\n");
	printf("}\n");
	printf("\n");

	return 0;
}

/* iterate over all operators;
 * provide precedence and [order] number with the callback
 */
static void iterops(void (*f)(char const* op, int prec, int num, void* data),
	void* data) {
	int p, i;
	for (p = 0; precs[p] != NULL; p++) {
		for (i = 0; precs[p][i] != NULL; i++) {
			(*f)(precs[p][i], p, i, data);
		}
	}
}

/* just dump (outout); delimit reasonably */
static void dumpop(char const* op, int prec, int num, void* data) {
	fprintf((FILE*)data, "%s%s", ((num > 0) ? " " :
		((prec > 0) ? "\n" : "")), op);
}

/* generate all operator pairs
 * (i.e. including {a,b} _and_ {b,a}, _and_ {a,a})
 */
static void genoppairs(void (*f)(char const* op1, int prec1, int num1,
	char const* op2, int prec2, int num2, void* data), void* data) {
	void* data2[2];
	data2[0] = &f;
	data2[1] = data;
	iterops(genoppairs2, data2);
}
static void genoppairs2(char const* op, int prec, int num, void* data) {
	void* data2[4];
	data2[0] = (void*)op;
	data2[1] = &prec;
	data2[2] = &num;
	data2[3] = data;
	iterops(genoppairs3, data2);
}
static void genoppairs3(char const* op, int prec, int num, void* data) {
	/* [adapter] */
	(**(void (**)(char const* op1, int prec1, int num1,
		char const* op2, int prec2, int num2, void* data))
		((void**)((void**)data)[3])[0])(
		(char const*)((void**)data)[0], *(int const*)((void**)data)[1],
		*(int const*)((void**)data)[2],
		op, prec, num, ((void**)((void**)data)[3])[1]);
}

/* generate test method for one operator pair;
 * consider possible divide-by-zero problems (skip test in that case);
 * note that the individual functions should be reasonably small
 * in order to be optimizable etc.
 */
static void gentest(char const* op1, int prec1, int num1,
	char const* op2, int prec2, int num2, void* data) {

	/* need to check for division by zero with /, % */
	int avoidzero1 = ((prec1 == 0) && (num1 != 0));
	int avoidzero2 = ((prec2 == 0) && (num2 != 0));
	/* need to check for negative argument to <<, >> */
	int avoidneg1 = (prec1 == 2);
	int avoidneg2 = (prec2 == 2);

	fprintf((FILE*)data, "static int ");
	genname(op1, prec1, num1, op2, prec2, num2, data);
	fprintf((FILE*)data, "(int a, int b, int c) {\n");
	fprintf((FILE*)data, "\tint x, y, z;\n");
	if (avoidzero1 || avoidneg1) {
		fprintf((FILE*)data, "\tint t;\n");
	}
	if (verbose) {
		fprintf((FILE*)data, "\tprintf(\"%%d %%s %%d %%s %%d\\n\", ");
		fprintf((FILE*)data, "a, \"%s\", b, \"%s\", c);\n", op1, op2);
	}

	/* skip [possibly] exception generating code */
	if (avoidzero2) {
		fprintf((FILE*)data, "\tif (c == 0) {\n");
		fprintf((FILE*)data, "\t\treturn SKIP;\n");
		fprintf((FILE*)data, "\t}\n");
	}
	if (avoidneg2) {
		fprintf((FILE*)data, "\tif (c < 0) {\n");
		fprintf((FILE*)data, "\t\treturn SKIP;\n");
		fprintf((FILE*)data, "\t}\n");
	}
	if (avoidzero1) {
		fprintf((FILE*)data, "\tif (b == 0) {\n");
		fprintf((FILE*)data, "\t\treturn SKIP;\n");
		fprintf((FILE*)data, "\t}\n");
		fprintf((FILE*)data, "\tt = (b %s c);\n", op2);
		fprintf((FILE*)data, "\tif (t == 0) {\n");
		fprintf((FILE*)data, "\t\treturn SKIP;\n");
		fprintf((FILE*)data, "\t}\n");
	}
	if (avoidneg1) {
		fprintf((FILE*)data, "\tif (b < 0) {\n");
		fprintf((FILE*)data, "\t\treturn SKIP;\n");
		fprintf((FILE*)data, "\t}\n");
		fprintf((FILE*)data, "\tt = (b %s c);\n", op2);
		fprintf((FILE*)data, "\tif (t < 0) {\n");
		fprintf((FILE*)data, "\t\treturn SKIP;\n");
		fprintf((FILE*)data, "\t}\n");
	}

	/* _the_ test */
	fprintf((FILE*)data, "\tx = (a %s b %s c);\n", op1, op2);
	/* the two references */
	fprintf((FILE*)data, "\ty = ((a %s b) %s c);\n", op1, op2);
	fprintf((FILE*)data, "\tz = (a %s (b %s c));\n", op1, op2);

	fprintf((FILE*)data, "\tif ((x == y) && (x == z)) {\n");
	fprintf((FILE*)data, "\t\treturn SAME;\n");
	fprintf((FILE*)data, "\t}\n");
	fprintf((FILE*)data, "\tif ((x != y) && (x != z)) {\n");
	fprintf((FILE*)data, "\t\treturn ILLG;\n");
	fprintf((FILE*)data, "\t}\n");

	if (prec1 != prec2) {
		fprintf((FILE*)data, "\tif (x != %s) {\n",
			((prec1 < prec2) ? "y" : "z"));
		fprintf((FILE*)data, "\t\treturn WRNG;\n");
		fprintf((FILE*)data, "\t}\n");
	} else {
		/* left-to-right associative */
		fprintf((FILE*)data, "\tif (x != y) {\n");
		fprintf((FILE*)data, "\t\treturn ASSC;\n");
		fprintf((FILE*)data, "\t}\n");
	}

	fprintf((FILE*)data, "\treturn OK;\n");
	fprintf((FILE*)data, "}\n");
	fprintf((FILE*)data, "\n");
}

/* generate a unique method name; include both precedence/number pairs */
static void genname(char const* op1, int prec1, int num1,
	char const* op2, int prec2, int num2, void* data) {
	(void)op1;
	(void)op2;
	fprintf((FILE*)data, "f_%d_%d_%d_%d", prec1, num1, prec2, num2);
}
static void genname2(char const* op1, int prec1, int num1,
	char const* op2, int prec2, int num2, void* data) {
	fprintf((FILE*)data, "\t");
	genname(op1, prec1, num1, op2, prec2, num2, data);
	fprintf((FILE*)data, ",\n");
}

static void genfunclist(char const* name, FILE* out) {
	/* keep this public, for optimizer */
	fprintf(out, "/");
	fprintf(out, "* static *");
	fprintf(out, "/ int (*const %s[])(int a, int b, int c) = {\n", name);
	genoppairs(genname2, out);
	fprintf(out, "\tNULL\n");
	fprintf(out, "};\n");
}

static void genperms3(int a, int b, int c) {
	printf("\tn += (runfuncs(%d, %d, %d));\n", a, b, c);
	printf("\tn += (runfuncs(%d, %d, %d));\n", a, c, b);
	printf("\tn += (runfuncs(%d, %d, %d));\n", b, a, c);
	printf("\tn += (runfuncs(%d, %d, %d));\n", b, c, a);
	printf("\tn += (runfuncs(%d, %d, %d));\n", c, a, b);
	printf("\tn += (runfuncs(%d, %d, %d));\n", c, b, a);
}

static void genperms2(int a, int b) {
	printf("\tn += (runfuncs(%d, %d, %d));\n", a, a, b);
	printf("\tn += (runfuncs(%d, %d, %d));\n", a, b, a);
	printf("\tn += (runfuncs(%d, %d, %d));\n", b, a, a);
	printf("\tn += (runfuncs(%d, %d, %d));\n", b, b, a);
	printf("\tn += (runfuncs(%d, %d, %d));\n", b, a, b);
	printf("\tn += (runfuncs(%d, %d, %d));\n", a, b, b);
}

