shithub: mc

Download patch

ref: 321aec6bf2b82698196a94725e37cee135b1fe2b
parent: 4a6a372ad70eb3bde6f0fa6bac7760383b6f6ea8
author: mural <mura@ctli.io>
date: Wed Apr 15 18:38:24 EDT 2020

Support or-pattern

--- a/mi/match.c
+++ b/mi/match.c
@@ -82,6 +82,8 @@
 	Node **cap; 		/* the captured variables of the pattern of this match clause */
 	size_t ncap;
 	Dtree *final;		/* final state, shared by all Frontiers for a specific (indxed by i) match clause */
+
+	struct Frontier *next;
 } Frontier;
 
 static Node *
@@ -409,6 +411,36 @@
 	return 0;
 }
 
+static Frontier *
+mkfrontier(int i, Node *lbl)
+{
+	Frontier *fs;
+
+	fs = zalloc(sizeof(Frontier));
+	fs->i = i;
+	fs->lbl = lbl;
+	fs->final = mkdtree(lbl->loc, lbl);
+	fs->final->accept = 1;
+	return fs;;
+}
+
+/*
+ * All immutable fields are shared; mutable fields must be cloned *
+ */
+static Frontier *
+frontierdup(Frontier *fs)
+{
+	Frontier *out;
+
+	out = mkfrontier(fs->i, fs->lbl);
+	out->slot = memdup(fs->slot, fs->nslot*sizeof(fs->slot[0]));
+	out->nslot = fs->nslot;
+	out->cap = memdup(fs->cap, fs->ncap*sizeof(fs->cap[0]));
+	out->ncap = fs->ncap;
+	return out;
+}
+
+
 /* addrec generates a list of slots for a Frontier by walking a pattern tree.
  * It collects only the terminal patterns like union tags and literals.
  * Non-terminal patterns like tuple/struct/array help encode the path only.
@@ -421,9 +453,16 @@
 	Node *memb, *name, *tagid, *p, *v, *lit, *dcl, *asn, *deref;
 	Ucon *uc;
 	char *s;
+	Frontier *next;
 
 	pat = fold(pat, 1);
 	switch (exprop(pat)) {
+	case Olor:
+		addrec(fs, pat->expr.args[1], val, path);
+		next = frontierdup(fs);
+		fs->next = next;
+		addrec(next, pat->expr.args[0], val, path);
+		break;
 	case Ogap:
 		lappend(&fs->slot, &fs->nslot, mkslot(path, pat, val));
 		break;
@@ -515,13 +554,12 @@
 {
 	Frontier *fs;
 
-	fs = zalloc(sizeof(Frontier));
-	fs->i = i;
-	fs->lbl = lbl;
-	fs->final = mkdtree(lbl->loc, lbl);
-	fs->final->accept = 1;
+	fs = mkfrontier(i, lbl);
 	addrec(fs, pat, val, mkpath(NULL, 0));
-	lappend(frontier, nfrontier, fs);
+	while (fs != NULL) {
+		lappend(frontier, nfrontier, fs);
+		fs = fs->next;
+	}
 }
 
 /*
@@ -863,6 +901,12 @@
 	dt->emitted = 0;
 }
 
+static int
+capeq(Node *a, Node *b)
+{
+	return 1;
+}
+
 Dtree *
 gendtree(Node *m, Node *val, Node **lbl, size_t nlbl)
 {
@@ -869,8 +913,8 @@
 	Dtree *root;
 	Node **pat;
 	size_t npat;
-	size_t i;
-	Frontier **frontier;
+	size_t i, j;
+	Frontier **frontier, *cur, *last;
 	size_t nfrontier;
 	FILE *csv;
 	char *dbgloc, *dbgfn, *dbgln;
@@ -887,8 +931,22 @@
 	for (i = 0; i < npat; i++) {
 		genfrontier(i, val, pat[i]->match.pat, lbl[i], &frontier, &nfrontier);
 	}
+
+	// to determine if two different sets of captures come from a or-pattern, which is NOT allowed.
+	last = NULL;
 	for (i = 0; i < nfrontier; i++) {
-		addcapture(pat[i]->match.block, frontier[i]->cap, frontier[i]->ncap);
+		cur = frontier[i];
+		if (last && last->i == cur->i) {
+			if (last->ncap != cur->ncap)
+				fatal(pat[cur->i], "captured variables are not equally bound in all or-pattern of the same group");
+			for (j = 0; j < cur->ncap; j++) {
+				if (!capeq(last->cap[j], cur->cap[j]))
+					fatal(pat[cur->i], "captured variables are not equally bound in all or-pattern of the same group");
+			}
+		} else {
+			addcapture(pat[cur->i]->match.block, cur->cap, cur->ncap);
+		}
+		last = cur;
 	}
 	root = compile(frontier, nfrontier);
 
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -1531,10 +1531,13 @@
 
 	n = *np;
 	n = checkns(n, np);
+	n->expr.ispat = 1;
 	args = n->expr.args;
 	for (i = 0; i < n->expr.nargs; i++)
-		if (args[i]->type == Nexpr)
+		if (args[i]->type == Nexpr) {
+			args[i]->expr.ispat = 1;
 			inferpat(&args[i], val, bind, nbind);
+		}
 	switch (exprop(n)) {
 	case Otup:
 	case Ostruct:
@@ -1541,6 +1544,7 @@
 	case Oarr:
 	case Olit:
 	case Omemb:
+	case Olor:
 		infernode(np, NULL, NULL);
 		break;
 		/* arithmetic expressions just need to be constant */
@@ -1700,6 +1704,7 @@
 	case Obsreq:	/* @a >>= @a -> @a */
 		infersub(n, ret, sawret, &isconst);
 		t = type(args[0]);
+
 		constrain(n, t, traittab[Tcnum]);
 		constrain(n, t, traittab[Tcint]);
 		isconst = args[0]->expr.isconst;
@@ -1722,6 +1727,15 @@
 
 		/* operands same type, returning bool */
 	case Olor:	/* @a || @b -> bool */
+		if (n->expr.ispat) {
+			infersub(n, ret, sawret, &isconst);
+			t = type(args[0]);
+			for (i = 1; i < nargs; i++)
+				t = unify(n, t, type(args[i]));
+			n->expr.isconst = isconst;
+			settype(n, t);
+			break;
+		}
 	case Oland:	/* @a && @b -> bool */
 	case Oeq:	/* @a == @a -> bool */
 	case One:	/* @a != @a -> bool */
--- a/parse/parse.h
+++ b/parse/parse.h
@@ -237,6 +237,7 @@
 			Type *type;
 			Type *param;	/* for specialized traits, the primary param */
 			int isconst;
+			int ispat;
 			size_t did;	/* for Ovar, we want a mapping to the decl id */
 			size_t nargs;
 			Node *idx;	/* used when this is in an indexed initializer */
--- /dev/null
+++ b/test/matchor.myr
@@ -1,0 +1,45 @@
+use std
+
+
+const main = {
+	type foo = union
+		`Black
+		`Blue
+		`Green
+		`Red
+		`Yellow
+		`White
+	;;
+
+	match `Green
+	| `Black || `White: std.exit(1)
+	| `Blue || `Green || `Red: std.put("color\n")
+	| _: std.exit(1)
+	;;
+
+	match `std.Some 100
+	| `std.Some (100 || 200 || 300): std.put("hundreds\n")
+	| `std.Some _: std.exit(1)
+	| _: std.exit(1)
+	;;
+
+	match `std.Some (`std.Some 333, 123, 789)
+	| `std.Some (`std.Some (101||451||789||333), _, _): std.put("good #1\n")
+	| `std.Some (`std.Some (100||200), 222, 333): std.exit(1)
+	| `std.Some _: std.exit(1)
+	| `std.None: std.exit(1)
+	;;
+
+	match 4
+	| 1||2||4: std.put("good $2\n")
+	| _: std.exit(1)
+	;;
+
+	const a = 4
+	match 4
+	| 1||2||a: std.put("good $3\n")
+	| _: std.exit(1)
+	;;
+
+	std.put("all good\n")
+}
--- a/test/tests
+++ b/test/tests
@@ -127,6 +127,7 @@
 F matchmixed
 F matchoverlap
 B matchctup	P	'l = [0, 1, 2, 3] n = 5'
+B matchor	E	0
 B bigliteral	P	34359738368
 B fltabs	P	'42.0'
 B arraylit-ni	E	2