summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormural <mura@ctli.io>2020-04-15 22:38:24 +0800
committerOri Bernstein <ori@eigenstate.org>2020-05-05 20:33:19 -0700
commit321aec6bf2b82698196a94725e37cee135b1fe2b (patch)
tree83aade6691594d565e78691fdd29a883478f05f0
parent4a6a372ad70eb3bde6f0fa6bac7760383b6f6ea8 (diff)
downloadmc-321aec6bf2b82698196a94725e37cee135b1fe2b.tar.gz
Support or-pattern
-rw-r--r--mi/match.c76
-rw-r--r--parse/infer.c16
-rw-r--r--parse/parse.h1
-rw-r--r--test/matchor.myr45
-rw-r--r--test/tests1
5 files changed, 129 insertions, 10 deletions
diff --git a/mi/match.c b/mi/match.c
index 16fe04d..d07f516 100644
--- a/mi/match.c
+++ b/mi/match.c
@@ -82,6 +82,8 @@ typedef struct Frontier {
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 @@ nconstructors(Type *t)
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 @@ addrec(Frontier *fs, Node *pat, Node *val, Path *path)
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 @@ genfrontier(int i, Node *val, Node *pat, Node *lbl, Frontier ***frontier, size_t
{
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,14 +901,20 @@ clearemit(Dtree *dt)
dt->emitted = 0;
}
+static int
+capeq(Node *a, Node *b)
+{
+ return 1;
+}
+
Dtree *
gendtree(Node *m, Node *val, Node **lbl, size_t nlbl)
{
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 @@ gendtree(Node *m, Node *val, Node **lbl, size_t nlbl)
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);
diff --git a/parse/infer.c b/parse/infer.c
index 0053c19..5150621 100644
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -1531,16 +1531,20 @@ inferpat(Node **np, Node *val, Node ***bind, size_t *nbind)
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:
case Oarr:
case Olit:
case Omemb:
+ case Olor:
infernode(np, NULL, NULL);
break;
/* arithmetic expressions just need to be constant */
@@ -1700,6 +1704,7 @@ inferexpr(Node **np, Type *ret, int *sawret)
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 @@ inferexpr(Node **np, Type *ret, int *sawret)
/* 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 */
diff --git a/parse/parse.h b/parse/parse.h
index 4fd6fd3..fa18ecd 100644
--- a/parse/parse.h
+++ b/parse/parse.h
@@ -237,6 +237,7 @@ struct Node {
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 */
diff --git a/test/matchor.myr b/test/matchor.myr
new file mode 100644
index 0000000..0ac8b7a
--- /dev/null
+++ b/test/matchor.myr
@@ -0,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")
+}
diff --git a/test/tests b/test/tests
index 9ffa747..20b67fe 100644
--- a/test/tests
+++ b/test/tests
@@ -127,6 +127,7 @@ B matchnest P 'abcdef'
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