#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "util.h"
#include "parse.h"
#include "mi.h"

#define V1
/**
 * TODO
 * Recursively check the types of the pattern and the subject tree.
 *
 */

File file;
char debugopt[128];

typedef struct Dtree Dtree;
extern Dtree *gendtree(Node *m, Node *val, Node **lbl, size_t nlbl);
extern void dtreedump(FILE *fd, Dtree *dt);


static char *
errorf(char *fmt, ...)
{
	va_list ap;
	char *p;
	size_t n;

	p = malloc(2048);
	if (p == NULL)
		return "(NULL)";

	va_start(ap, fmt);
	n = vsnprintf(p, 2048, fmt, ap);
	va_end(ap);
	if (n > 0) {
		p = realloc(p, n+1);
	} else {
		free(p);
		p = "(NULL)";
	}
	return p;
}

static Type *
installucons(Type *t)
{
	Type *b;
	size_t i;

	if (!t)
		return NULL;
	b = tybase(t);
	switch (b->type) {
	case Tystruct:
		for (i = 0; i < b->nmemb; i++)
			installucons(b->sdecls[i]->decl.type);
		break;
	case Tyunion:
		for (i = 0; i < b->nmemb; i++) {
			if (!b->udecls[i]->utype)
				b->udecls[i]->utype = b;
			b->udecls[i]->id = i;
		}
		break;
	default:
		break;
	}
	return t;
}

static Node *
mkdummymatch(Node *p)
{
	Node *b;

	b = mkblock(Zloc, mkstab(0));
	b->block.stmts = NULL;
	b->block.nstmts = 0;

	return mkmatch(Zloc, p, b);
}

static Node *
ty(Node *n, Type *t)
{
	switch (n->type) {
	case Nlit:
		n->lit.type = t;
		break;
	case Nexpr:
		n->expr.type = t;
	default:
		;
	}
	return n;
}

static char *
exprequal(Node *a, Node *b, int depth)
{
	size_t i;
	char *err;

	if (a == NULL && b == NULL)
		return NULL;

	if (a == NULL || b == NULL)
		return "either one is null";

	if (false && a->nid != b->nid)
		return errorf("nid is not matched. (%d) want %d, got %d", depth, a->nid, b->nid);

	if (a->type != b->type)
		return errorf("ntype is not matched. (%d) want %s, got %s", depth, nodestr[a->type], nodestr[b->type]);

	switch (a->type) {
	case Nexpr:
		if (exprop(a) != exprop(b))
			return errorf("op is not matched. (%d) want %s, got %s", depth, opstr[exprop(a)], opstr[exprop(b)]);

		if (a->expr.nargs != b->expr.nargs) {
			fprintf(stderr, "op:%s\n", opstr[exprop(a)]);
			return errorf("nargs is not matched. (%d) want %d, got %d", depth, a->expr.nargs, b->expr.nargs);
		}

		for (i = 0; i < a->expr.nargs; i++) {
			err = exprequal(a->expr.args[i], b->expr.args[i], depth+1);
			if (err != NULL)
				return err;
		}
		break;
	case Nlit:
		switch (a->lit.littype) {
		case Lint:
			if (a->lit.intval != b->lit.intval)
				return errorf("lit.intval is not matched. (%d) want %d, got %d", depth, a->lit.intval, b->lit.intval);
			break;
		default:
		return errorf("unexpected littype: %s", nodestr[a->type]);
		}
		break;
	case Nname:
		break;
	default:
		return errorf("unexpected ntype: %s", nodestr[a->type]);
	}

	return NULL;
}

static char *
dtequal(Dtree *a, Dtree *b, int depth)
{
	size_t i;
	char *err;

	if (a == NULL && b == NULL)
		return NULL;
	if (a == NULL || b == NULL)
		return "either one is null";

	if (a->id != b->id)
		return errorf("id is not matched. (depth:%d) want %d, got %d", depth, a->id, b->id);

	if (a->nconstructors != b->nconstructors)
		return errorf("nconstructors is not matched. (depth:%d id:%d) want %ld, got %ld", depth, a->id, a->nconstructors, b->nconstructors);

	if (a->accept != b->accept)
		return errorf("accept is not matched. (depth:%d id:%d) want %ld, got %ld", depth, a->id, a->accept, b->accept);

	if (a->emitted != b->emitted)
		return errorf("emitted is not matched. (depth:%d id:%d) want %ld, got %ld", depth, a->id, a->emitted, b->emitted);

	err = exprequal(a->load, b->load, 0);
	if (err != NULL) {
		fprintf(stderr, "WANT:\n");
		dumpn(a->load, stderr);
		fprintf(stderr, "GOT:\n");
		dumpn(b->load, stderr);
		return errorf("load is not matched. (depth:%d id:%d) want %p, got %p, %s", depth, a->id, a->load, b->load, err);
	}

	if (a->nnext != b->nnext)
		return errorf("nnext is not matched. (depth:%d id:%d) want %d, got %d", depth, a->id, a->nnext, b->nnext);

	for (i = 0; i < a->npat; i++) {
		err = exprequal(a->pat[i], b->pat[i], 0);
		if (err != NULL) {
			fprintf(stderr, "WANT:\n");
			dumpn(a->pat[i], stderr);
			fprintf(stderr, "GOT:\n");
			dumpn(b->pat[i], stderr);
			return errorf("pat is not matched. (depth:%d id:%d) want %p, got %p, %s", depth, a->id, a->pat[i], b->pat[i], err);
		}
	}
	for (i = 0; i < a->nnext; i++) {
		err = dtequal(a->next[i], b->next[i], depth+1);
		if (err != NULL)
			return errorf("next[%d] is not matched. (depth:%d id:%d) want %p, got %p, %s", i, depth, a->id, a->next[i], b->next[i], err);
	}
	err = dtequal(a->any, b->any, depth+1);
	if (err != NULL)
		return errorf("any is not matched. (depth:%d id:%d) want %p, got %p, %s", depth, a->id, a->any, b->any, err);

	return NULL;
}



static char *
test_match(int idx, Node *val, Node **pat, Dtree *want)
{
	Dtree *dt;
	Node *m, *v, *r, **lbl, **matches;
	size_t i, nlbl, npat, nmatches;
	char *err;

	matches = NULL;
	nmatches = 0;
	for (npat = 0; pat[npat] != NULL; npat++) {
		lappend(&matches, &nmatches, mkdummymatch(pat[npat]));
	}

	m = mkmatchstmt(Zloc, val, matches, nmatches);
	r = val;
	v = gentemp(r->loc, r->expr.type, NULL);


	if (getenv("D")) {
		fprintf(stderr, "[%.3d]>\n", idx);
		dumpn(m, stdout);
	}

	if (1) {
		lbl = NULL;
		nlbl = 0;
		for (i = 0; i < nmatches; i++) {
			lappend(&lbl, &nlbl, genlbl(pat[i]->match.block->loc));
		}

		dt = gendtree(m, v, lbl, nlbl);
		if (getenv("d")) {
			fprintf(stderr, "dtree >>\n");
			dtreedump(stderr, dt);
		}

		err = dtequal(want, dt, 0);
		if (err)
			return err;
	}

	if (getenv("G")) {
		Node **matches = NULL;
		size_t nmatches = 0;
		genmatch(m, v, &matches, &nmatches);

		fprintf(stdout, "===========\n");
		for (i = 0; i < nmatches; i++) {
			dumpn(matches[i], stdout);
		}
	}

	return NULL;
}

typedef struct Test {
	char *name;
	char *desc;
	Node *val;
	Node **pats;
	Dtree *dt;
} Test;

inline static Node *
setnode(Node **dst, Node *src)
{
	*dst = src;
	return *dst;
}

inline static Node *
getnode(Node *n)
{
	return n;
}

int
main(int argc, char **argv)
{
	size_t i;
	char *err;
	Node *t, *p_, *p0, *p1, *p2;

#define P(x) getnode(p##x)

#define InitP0(p) setnode(&p0, p)
#define InitP1(p) setnode(&p1, p)
#define InitP2(p) setnode(&p2, p)
#define InitP_(p) setnode(&p_, p)


#define InitT(v) setnode(&t, v)
#define T getnode(t)


	Type *_int32 = mktype(Zloc, Tyint32);
	Type *_int64 = mktype(Zloc, Tyint64);

	Type *_int32t1 = mktytuple(Zloc, (Type*[]){_int32}, 1);
	Type *_int32t2 = mktytuple(Zloc, (Type*[]){_int32, _int32}, 2);
	Type *_int32t3 = mktytuple(Zloc, (Type*[]){_int32, _int32, _int32}, 3);

	Type *_int32s1 = mktystruct(Zloc, (Node*[]){mkdecl(Zloc, mkname(Zloc, "foo"), _int32)}, 1);
	Type *_enum1 = installucons(mktyunion(Zloc, (Ucon*[]){mkucon(Zloc, mkname(Zloc, "Foo"), NULL, NULL)}, 1));
	Type *_enum2 = installucons(mktyunion(Zloc,
					      (Ucon*[]){
						      mkucon(Zloc, mkname(Zloc, "Foo"), NULL, NULL),
						      mkucon(Zloc, mkname(Zloc, "Bar"), NULL, NULL)},
						      2));
	Type *_enum3 = installucons(mktyunion(Zloc,
					      (Ucon*[]){
						      mkucon(Zloc, mkname(Zloc, "Foo"), NULL, NULL),
						      mkucon(Zloc, mkname(Zloc, "Bar"), NULL, NULL),
						      mkucon(Zloc, mkname(Zloc, "Baz"), NULL, NULL)},
						      3));

	Type *_int32u1 = mktyunion(Zloc, (Ucon*[]){
		mkucon(Zloc, mkname(Zloc, "Foo"), NULL, _int32t1),
	}, 1);

	Type *_int32a1 = mktyslice(Zloc, _int32);


	Type *_bug001u = installucons(mktyunion(Zloc, (Ucon*[]){
			mkucon(Zloc, mkname(Zloc, "Foo"), NULL, _int32t1),
			mkucon(Zloc, mkname(Zloc, "Bar"), NULL, NULL)
		}, 2));
	Type *_bug001s = mktystruct(Zloc, (Node*[]){
		mkdecl(Zloc, mkname(Zloc, "s1_slice"), _int32a1),
		mkdecl(Zloc, mkname(Zloc, "s1_int32"), _int32),
	}, 2);

	Type *_bug002s = mktystruct(Zloc, (Node*[]){
		mkdecl(Zloc, mkname(Zloc, "s2_ufoo"), _bug001u),
		mkdecl(Zloc, mkname(Zloc, "s2_sbar"), _bug001s),
		mkdecl(Zloc, mkname(Zloc, "s2_int32"), _int32),
	}, 3);


	Dtree *dt11 = &(Dtree){
		.id = 11,
		.any = NULL,
	};
	Dtree *dt8 = &(Dtree){
		.id = 8,
		.any =dt11,
	};
	dt11->any = dt8;

	Test tests[] = {
		{
			.name = "int32 matched by 1 wildcard",
			.val  = InitT(gentemp(Zloc, _int32, NULL)),
			.pats = (Node*[]){
				ty(mkexpr(Zloc, Ogap, NULL), _int32),
				NULL,
			},
			.dt = &(Dtree){
				.id = 0,
				.accept = 1,
			},
		},
		{
			.name = "int32 matched by 1 capture variable",
			.val = InitT(gentemp(Zloc, _int32, NULL)),
			.pats = (Node*[]){
				InitP0(ty(mkexpr(Zloc, Ovar, mkname(Zloc, "foovar"), NULL), _int32)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 0,
				.accept = 1,
			},
		},
		{
			.name = "int32 matched by 1 literals",
			.val = InitT(gentemp(Zloc, _int32, NULL)),
			.pats = (Node*[]){
				InitP0(ty(mkexpr(Zloc, Olit, mkint(Zloc, 123), NULL), _int32)),
				InitP_(ty(mkexpr(Zloc, Ogap, NULL), _int32)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 2,
				.load = gentemp(Zloc, _int32, NULL),
				.nconstructors = 4294967296,
				.nnext = 1,
				.npat = 1,
				.pat = (Node*[]){
					[0] = p0,
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 0,
						.accept = 1,
					},
				},
				.any = &(Dtree){
					.id = 1,
					.accept = 1,
				},
			},
		},
		{
			.name = "int32 matched by 2 literals",
			.val = InitT(gentemp(Zloc, _int32, NULL)),
			.pats = (Node*[]){
				InitP0(ty(mkexpr(Zloc, Olit, mkint(Zloc, 123), NULL), _int32)),
				InitP1(ty(mkexpr(Zloc, Olit, mkint(Zloc, 456), NULL), _int32)),
				InitP_(ty(mkexpr(Zloc, Ogap, NULL), _int32)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 3,
				.load = gentemp(Zloc, _int32, NULL),
				.nconstructors = 4294967296,
				.npat = 2,
				.nnext = 2,
				.pat = (Node*[]){
					[0] = P(0),
					[1] = P(1),
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 0,
						.accept = 1,
					},
					[1] = &(Dtree){
						.id = 1,
						.accept = 1,
					},
				},
				.any = &(Dtree){
					.id = 2,
					.accept = 1,
				},
			},
		},
		{
			.name = "1-tuple, matched by wildcard only",
			.val = InitT(gentemp(Zloc, _int32t1, NULL)),
			.pats = (Node*[]){
				ty(mkexpr(Zloc, Otup, ty(mkexpr(Zloc, Ogap, NULL), _int32), NULL), _int32t1),
				NULL,
			},
			.dt = &(Dtree){
				.id = 0,
				.accept = 1,
				.nnext = 0,
				.npat = 0,
				.any = NULL,
			},
		},
		{
			.name = "1-tuple",
			.val = InitT(gentemp(Zloc, _int32t1, NULL)),
			.pats = (Node *[]){
				InitP0(ty(mkexpr(Zloc, Otup, ty(mkexpr(Zloc, Olit, mkint(Zloc, 123), NULL), _int32), NULL), _int32t1)),
				InitP1(ty(mkexpr(Zloc, Ogap, NULL), _int32t1)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 2,
				.load = ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 0), NULL), _int32),
				.nconstructors = 4294967296,
				.nnext = 1,
				.npat = 1,
				.pat =  (Node*[]){
					P(0)->expr.args[0],
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 0,
						.accept = 1,
					},
				},
				.any = &(Dtree){
					.id = 1,
					.accept = 1
				},
			},
		},
		{
			.name = "2-tuple",
			.val = InitT(gentemp(Zloc, _int32t2, NULL)),
			.pats = (Node *[]){
				/**
				 * | (123, 456):
				 */
				InitP0(ty(mkexpr(Zloc, Otup,
					     ty(mkexpr(Zloc, Olit, mkint(Zloc, 123), NULL), _int32),
					     ty(mkexpr(Zloc, Olit, mkint(Zloc, 456), NULL), _int32),
					     NULL), _int32t2)),
				/**
				 * | (_, _):
				 */
				InitP1(ty(mkexpr(Zloc, Ogap, NULL), _int32t2)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 3,
				.load = ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 0), NULL), _int32),
				.nconstructors = 4294967296,
				.nnext = 1,
				.npat = 1,
				.pat = (Node*[]){
					P(0)->expr.args[0],
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 2,
						.load =ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 1), NULL), _int32),
						.nconstructors = 4294967296,
						.nnext = 1,
						.npat = 1,
						.pat = (Node*[]){
							P(0)->expr.args[1],
						},
						.next = (Dtree*[]){
							[0] = &(Dtree){
								.id = 0,
								.accept = 1,
							},
						},
						.any = &(Dtree){
							.id = 1,
							.accept = 1,
						},
					},
				},
				.any = &(Dtree){
					.id = 1,
					.accept = 1,
				}
			},
		},
		{
			.name = "3-tuple",
			.val = InitT(gentemp(Zloc, _int32t3, NULL)),
			.pats = (Node *[]){
				/**
				 * | (123, 456):
				 */
				InitP0(ty(mkexpr(Zloc, Otup,
					     ty(mkexpr(Zloc, Olit, mkint(Zloc, 123), NULL), _int32),
					     ty(mkexpr(Zloc, Olit, mkint(Zloc, 456), NULL), _int32),
					     ty(mkexpr(Zloc, Olit, mkint(Zloc, 789), NULL), _int32),
					     NULL), _int32t3)),
				/**
				 * | (_, _):
				 */
				InitP1(ty(mkexpr(Zloc, Ogap, NULL), _int32t3)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 4,
				.load = ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 0), NULL), _int32),
				.nconstructors = 4294967296,
				.nnext = 1,
				.npat = 1,
				.pat = (Node*[]){
					P(0)->expr.args[0],
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 3,
						.load = ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 1), NULL), _int32),
						.nconstructors = 4294967296,
						.nnext = 1,
						.npat = 1,
						.pat = (Node*[]){
							P(0)->expr.args[1],
						},
						.next = (Dtree*[]){
							[0] = &(Dtree){
								.id = 2,
								.load = ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 2), NULL), _int32),
								.nconstructors = 4294967296,
								.nnext = 1,
								.npat = 1,
								.pat = (Node*[]){
									P(0)->expr.args[2],
								},
								.next = (Dtree*[]){
									[0] = &(Dtree){
										.id = 0,
										.accept = 1,
									}
								},
								.any = &(Dtree){
									.id = 1,
									.accept = 1,
								},
							},
						},
						.any = &(Dtree){
							.id = 1,
							.accept = 1,
						},
					},
				},
				.any = &(Dtree){
					.id = 1,
					.accept =1,
				},
			},
		},
		{
			.name = "3-tuple-3-pat",
			.val = InitT(gentemp(Zloc, _int32t3, NULL)),
			.pats = (Node *[]){
				/**
				 * | (123, 456):
				 */
				InitP0(ty(mkexpr(Zloc, Otup,
					     ty(mkexpr(Zloc, Olit, mkint(Zloc, 101), NULL), _int32),
					     ty(mkexpr(Zloc, Olit, mkint(Zloc, 102), NULL), _int32),
					     ty(mkexpr(Zloc, Olit, mkint(Zloc, 103), NULL), _int32),
					     NULL), _int32t3)),
				InitP1(ty(mkexpr(Zloc, Otup,
					       ty(mkexpr(Zloc, Olit, mkint(Zloc, 201), NULL), _int32),
					       ty(mkexpr(Zloc, Olit, mkint(Zloc, 202), NULL), _int32),
					       ty(mkexpr(Zloc, Olit, mkint(Zloc, 203), NULL), _int32),
					       NULL), _int32t3)),
				InitP2(ty(mkexpr(Zloc, Otup,
					       ty(mkexpr(Zloc, Olit, mkint(Zloc, 301), NULL), _int32),
					       ty(mkexpr(Zloc, Olit, mkint(Zloc, 302), NULL), _int32),
					       ty(mkexpr(Zloc, Olit, mkint(Zloc, 303), NULL), _int32),
					       NULL), _int32t3)),

				/**
				 * | (_, _):
				 */
				InitP_(ty(mkexpr(Zloc, Ogap, NULL), _int32t3)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 10,
				.load = ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 0), NULL), _int32),
				.nconstructors = 4294967296,
				.nnext = 3,
				.npat = 3,
				.pat = (Node*[]){
					P(0)->expr.args[0],
					P(1)->expr.args[0],
					P(2)->expr.args[0],
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 5,
						.load = ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 1), NULL), _int32),
						.nconstructors = 4294967296,
						.nnext = 1,
						.npat = 1,
						.pat = (Node*[]){
							P(0)->expr.args[1],
						},
						.next = (Dtree*[]){
							[0] = &(Dtree){
								.id = 4,
								.load = ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 2), NULL), _int32),
								.nconstructors = 4294967296,
								.nnext = 1,
								.npat = 1,
								.pat = (Node*[]){
									P(0)->expr.args[2],
								},
								.next = (Dtree*[]){
									[0] = &(Dtree){
										.id = 0,
										.accept = 1,
									},
								},
								.any = &(Dtree){
									.id = 3,
									.accept = 1,
								},
							},
						},
						.any = &(Dtree){
							.id = 3,
							.accept = 1,
						},
					},
					[1] = &(Dtree){
						.id = 7,
						.load = ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 1), NULL), _int32),
						.nconstructors = 4294967296,
						.nnext = 1,
						.npat = 1,
						.pat = (Node*[]){
							P(1)->expr.args[1],
						},
						.next = (Dtree*[]){
							[0] = &(Dtree){
								.id = 6,
								.load = ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 2), NULL), _int32),
								.nconstructors = 4294967296,
								.nnext = 1,
								.npat = 1,
								.pat = (Node*[]){
									P(1)->expr.args[2],
								},
								.next = (Dtree*[]){
									[0] = &(Dtree){
										.id = 1,
										.accept = 1,
									},
								},
								.any = &(Dtree){
									.id = 3,
									.accept = 1,
								},
							},
						},
						.any = &(Dtree){
							.id = 3,
							.accept = 1,
						},
					},
					[2] = &(Dtree){
						.id = 9,
						.load = ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 1), NULL), _int32),
						.nconstructors = 4294967296,
						.nnext = 1,
						.npat = 1,
						.pat = (Node*[]){
							P(2)->expr.args[1],
						},
						.next = (Dtree*[]){
							[0] = &(Dtree){
								.id = 8,
								.load = ty(mkexpr(Zloc, Otupget, T, mkintlit(Zloc, 2), NULL), _int32),
								.nconstructors = 4294967296,
								.nnext = 1,
								.npat = 1,
								.pat = (Node*[]){
									P(2)->expr.args[2],
								},
								.next = (Dtree*[]){
									[0] = &(Dtree){
										.id = 2,
										.accept = 1,
									},
								},
								.any = &(Dtree){
									.id = 3,
									.accept = 1,
								},
							},
						},
						.any = &(Dtree){
							.id = 3,
							.accept = 1,
						},
					},
				},
				.any = &(Dtree){
					.id = 3,
					.accept = 1,
				},
			},
		},

		{
			.name = "1-int32-struct",
			.val = InitT(gentemp(Zloc, _int32s1, NULL)),
			.pats = (Node*[]){
				InitP0(ty(mkexprl(Zloc, Ostruct, (Node*[]){mkidxinit(Zloc, mkname(Zloc, "foo"), mkintlit(Zloc, 123))}, 1), _int32s1)),
				InitP_(ty(mkexpr(Zloc, Ogap, NULL), _int32s1)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 2,
				.load = ty(mkexpr(Zloc, Omemb, T, tybase(exprtype(P(0)))->sdecls[0]->decl.name, NULL), _int32),
				.nconstructors = 4294967296,
				.nnext = 1,
				.npat = 1,
				.pat = (Node*[]){
					P(0)->expr.args[0],
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 0,
						.accept = 1,
					},
				},
				.any = &(Dtree){
					.id = 1,
					.accept = 1,
				},
			},
		},
		{
			.name = "1-enum, matched by wildcard only",
			.val = InitT(gentemp(Zloc, _enum1, NULL)),
			.pats = (Node*[]){
				InitP_( ty(mkexpr(Zloc, Ogap, NULL), _enum1)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 0,
				.accept = 1,
			},
		},
		{
			.name = "1-enum, matched by a valid constructor",
			.val = InitT(gentemp(Zloc, _enum1, NULL)),
			.pats = (Node*[]){
				InitP0(ty(mkexpr(Zloc, Oucon, mkname(Zloc, "Foo"), NULL), _enum1)),
				InitP_(ty(mkexpr(Zloc, Ogap, NULL), _enum1)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 2,
				.nconstructors = 1,
				.load = ty(mkexpr(Zloc, Outag, T, NULL), _int32),
				.nnext = 1,
				.npat = 1,
				.pat = (Node*[]){
					/*
					 * the matcher will convert the Oucon expr to an Nlit for the Dtree
					 */
					ty(mkintlit(Zloc, finducon(_enum1, P(0)->expr.args[0])->id), _int32),
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 0,
						.accept = 1,
					},
				},
				.any = &(Dtree){
					.id = 1,
					.accept = 1,
				},
			},
		},
		/**
		 * match v : _enum2
		 * | `Foo
		 * | `Bar
		 * ;;
		 *
		 */
		{
			.name = "2-enum, matched by 2 valid constructors",
			.val = InitT(gentemp(Zloc, _enum2, NULL)),
			.pats = (Node*[]) {
				InitP0(ty(mkexpr(Zloc, Oucon, mkname(Zloc, "Foo"), NULL), _enum2)),
				InitP1(ty(mkexpr(Zloc, Oucon, mkname(Zloc, "Bar"), NULL), _enum2)),

				NULL,
			},
			.dt = &(Dtree){
				.id = 2,
				.load = ty(mkexpr(Zloc, Outag, T, NULL), _int32),
				.nconstructors = 2,
				.nnext = 2,
				.npat = 2,
				.pat = (Node*[]){
					ty(mkintlit(Zloc, finducon(_enum2, P(0)->expr.args[0])->id), _int32),
					ty(mkintlit(Zloc, finducon(_enum2, P(1)->expr.args[0])->id), _int32),
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 0,
						.accept = 1,
					},
					[1] = &(Dtree){
						.id = 1,
						.accept = 1,
					},
				},
				.any = NULL,
			},
		},
		{
			.name = "3-enum, matched by 3 valid constructors",
			.val = InitT(gentemp(Zloc, _enum3, NULL)),
			.pats = (Node*[]) {
				InitP0(ty(mkexpr(Zloc, Oucon, mkname(Zloc, "Foo"), NULL), _enum3)),
				InitP1(ty(mkexpr(Zloc, Oucon, mkname(Zloc, "Bar"), NULL), _enum3)),
				InitP2(ty(mkexpr(Zloc, Oucon, mkname(Zloc, "Baz"), NULL), _enum3)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 3,
				.load = ty(mkexpr(Zloc, Outag, T, NULL), _int32),
				.nconstructors = 3,
				.nnext = 3,
				.npat = 3,
				.pat = (Node*[]){
					ty(mkintlit(Zloc, finducon(_enum3, P(0)->expr.args[0])->id), _int32),
					ty(mkintlit(Zloc, finducon(_enum3, P(1)->expr.args[0])->id), _int32),
					ty(mkintlit(Zloc, finducon(_enum3, P(2)->expr.args[0])->id), _int32),
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 0,
						.accept = 1,
					},
					[1] = &(Dtree){
						.id = 1,
						.accept = 1,
					},
					[2] = &(Dtree){
						.id = 2,
						.accept = 1,
					},
				},
				.any = NULL,
			},
		},
		{
			.name = "1-int32-array, matched by an element",
			.val = InitT(gentemp(Zloc, _int32a1, NULL)),
			.pats = (Node*[]) {
				InitP0(ty(mkexpr(Zloc, Oarr,
						/**
						 * {.[0] = 123}
						 */
						mkidxinit(Zloc, mkintlit(Zloc, 0), mkintlit(Zloc, 123)),
						NULL),
					_int32s1)),
				InitP_(ty(mkexpr(Zloc, Ogap, NULL), _int32a1)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 2,
				.nconstructors = 4294967296,
				.load = ty(mkexpr(Zloc, Oidx, T, ty(mkintlit(Zloc, 0), _int64), NULL), _int32),
				.nnext = 1,
				.npat = 1,
				.pat = (Node*[]){
					P(0)->expr.args[0],
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 0,
						.accept = 1,
					},
				},
				.any = &(Dtree){
					.id = 1,
					.accept = 1,
				},
			},
		},
		/**
		 * | `Foo (int32)
		 */
		{
			.name = "1-union of 1-tuple, matched by wildcard only",
			.val = InitT(gentemp(Zloc, _int32u1, NULL)),
			.pats = (Node*[]){
				InitP_(ty(mkexpr(Zloc, Ogap, NULL), _int32u1)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 0,
				.accept = 1,
			},
		},
		{
			.name = "1-union of 1-tuple",
			.val = InitT(gentemp(Zloc, _int32u1, NULL)),
			.pats = (Node*[]){
				/**
				 * `Foo (123)
				 */
				InitP0(ty(mkexpr(Zloc, Oucon, mkname(Zloc, "Foo"),
					       ty(mkexpr(Zloc, Otup, ty(mkexpr(Zloc, Olit, mkint(Zloc, 123), NULL), _int32), NULL), _int32t1), NULL), _int32u1)),
				InitP_(ty(mkexpr(Zloc, Ogap, NULL), _int32u1)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 3,
				.load = ty(mkexpr(Zloc, Outag, T, NULL), _int32),
				.nconstructors = 1,
				.nnext = 1,
				.npat = 1,
				.pat = (Node*[]){
					/*
					 * the matcher will convert the Oucon expr to an Nlit for the Dtree
					 */
					ty(mkintlit(Zloc, finducon(_enum1, P(0)->expr.args[0])->id), _int32),
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 2,
						.load = ty(mkexpr(Zloc, Otupget,
								  ty(mkexpr(Zloc, Oudata, T, NULL), _int32t1),
								  mkintlit(Zloc, 0),
								  NULL), _int32),
						.nconstructors = 4294967296,
						.nnext = 1,
						.npat = 1,
						.pat = (Node*[]){
							P(0)->expr.args[1]->expr.args[0],
						},
						.next = (Dtree*[]){
							&(Dtree){
								.id = 0,
								.accept = 1,
							},
						},
						.any = &(Dtree){
							.id = 1,
							.accept = 1,
						},
					},
				},
				.any = &(Dtree){
					.id = 1,
					.accept = 1,
				},
			},
		},
		{
			.name = "bug1",
			.val = InitT(gentemp(Zloc, _bug002s, NULL)),
			.pats = (Node*[]){
				InitP0(ty(mkexprl(Zloc, Ostruct, (Node*[]){
					mkidxinit(Zloc, mkname(Zloc, "s2_ufoo"),
						  ty(mkexpr(Zloc, Oucon, mkname(Zloc, "Foo"),
							    mkintlit(Zloc, 123), NULL), _int32u1)
						 )}, 1), _bug002s)),
				InitP_(ty(mkexpr(Zloc, Ogap, NULL), _bug002s)),
				NULL,
			},
			.dt = &(Dtree){
				.id = 3,
				.load = ty(mkexpr(Zloc, Outag,
						  ty(mkexpr(Zloc, Omemb, T, tybase(exprtype(P(0)))->sdecls[0]->decl.name, NULL), _bug001u),
						  NULL), _int32),
				.nconstructors = 2,
				.nnext = 1,
				.npat = 1,
				.pat = (Node*[]){
					ty(mkintlit(Zloc, finducon(_bug001u, P(0)->expr.args[0]->expr.args[0])->id), _int32),
				},
				.next = (Dtree*[]){
					[0] = &(Dtree){
						.id = 2,
						.load = ty(mkexpr(Zloc, Oudata, ty(mkexpr(Zloc, Omemb, T, tybase(exprtype(P(0)))->sdecls[0]->decl.name, NULL), _int32), NULL), _int32t1),
						.nconstructors = 1,
						.nnext = 1,
						.npat = 1,
						.pat = (Node*[]){
							P(0)->expr.args[0]->expr.args[1],
						},
						.next = (Dtree*[]){
							[0] = &(Dtree){
								.id = 0,
								.accept = 1,
							},
						},
						.any = &(Dtree){
							.id = 1,
							.accept = 1,
						}
					},
				},
				.any = &(Dtree){
					.id = 1,
					.accept = 1,
				},
			},
		},

	};

	for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
		Test *t = &tests[i];
		char *name = t->name;
		char *filter = getenv("N");
		if (filter && !strstr(name, filter))
			continue;

		initfile(&file, errorf("(test-%d-%s)", i, name));
		fprintf(stderr, "[%03lu]------ %s ##\n", i, name);
		err = test_match(i, t->val, t->pats, t->dt);
		if (err) {
			fprintf(stderr, "FAILED id: %ld name: %s\n", i, name);
			fprintf(stderr, "%s\r\n", err);
			break;
		}
	}
	return 0;
}

