shithub: mc

Download patch

ref: 2a27b2d8ac101e17e300a2e25421fc25c403b05b
parent: da4798f000d99dd326a542758da83ad04be72f86
author: Ori Bernstein <ori@eigenstate.org>
date: Tue Nov 3 19:06:59 EST 2015

Rewrite pattern matching code.

    Much cleaner, and more efficient.

--- a/6/main.c
+++ b/6/main.c
@@ -98,7 +98,7 @@
     }
 }
 
-static char *gentemp(char *buf, size_t bufsz, char *path, char *suffix)
+static char *gentempfile(char *buf, size_t bufsz, char *path, char *suffix)
 {
     char *tmpdir;
     char *base;
@@ -235,7 +235,7 @@
             else
                 swapsuffix(buf, sizeof buf, ctx.args[i], ".myr", ".s");
         } else {
-            gentemp(buf, sizeof buf, ctx.args[i], ".s");
+            gentempfile(buf, sizeof buf, ctx.args[i], ".s");
         }
         genuse(ctx.args[i]);
         gen(file, buf);
--- a/6/simp.c
+++ b/6/simp.c
@@ -70,7 +70,6 @@
 static Node *simpcast(Simp *s, Node *val, Type *to);
 static Node *simpslice(Simp *s, Node *n, Node *dst);
 static Node *idxaddr(Simp *s, Node *seq, Node *idx);
-static void matchpattern(Simp *s, Node *pat, Node *val, Type *t, Node *iftrue, Node *iffalse);
 
 /* useful constants */
 Type *tyintptr;
@@ -285,23 +284,6 @@
     return n;
 }
 
-static Node *gentemp(Srcloc loc, Type *ty, Node **dcl)
-{
-    char buf[128];
-    static int nexttmp;
-    Node *t, *r, *n;
-
-    bprintf(buf, 128, ".t%d", nexttmp++);
-    n = mkname(loc, buf);
-    t = mkdecl(loc, n, ty);
-    r = mkexpr(loc, Ovar, n, NULL);
-    r->expr.type = t->decl.type;
-    r->expr.did = t->decl.did;
-    if (dcl)
-        *dcl = t;
-    return r;
-}
-
 static Node *temp(Simp *simp, Node *e)
 {
     Node *t, *dcl;
@@ -462,11 +444,14 @@
  */
 static void simpiter(Simp *s, Node *n)
 {
-    Node *lbody, *lstep, *lcond, *lmatch, *lend;
+    Node *lbody, *lload, *lstep, *lcond, *lmatch, *lend;
     Node *idx, *len, *dcl, *seq, *val, *done;
+    Node **cap, **out;
+    size_t i, ncap, nout;
     Node *zero;
 
     lbody = genlbl(n->loc);
+    lload = genlbl(n->loc);
     lstep = genlbl(n->loc);
     lcond = genlbl(n->loc);
     lmatch = genlbl(n->loc);
@@ -499,7 +484,19 @@
     cjmp(s, done, lmatch, lend);
     simp(s, lmatch);
     val = load(idxaddr(s, seq, idx));
-    matchpattern(s, n->iterstmt.elt, val, val->expr.type, lbody, lstep);
+
+    /* pattern match */
+    out = NULL;
+    nout = 0;
+    cap = NULL;
+    ncap = 0;
+    genonematch(n->iterstmt.elt, val, lload, lstep, &out, &nout, &cap, &ncap);
+    for (i = 0; i < nout; i++)
+        simp(s, out[i]);
+    simp(s, lload);
+    for (i = 0; i < ncap; i++)
+        simp(s, cap[i]);
+    jmp(s, lbody);
     simp(s, lend);
 
     s->nloopstep--;
@@ -518,144 +515,6 @@
     return word(uc->loc, uc->id);
 }
 
-static Node *patval(Simp *s, Node *n, Type *t)
-{
-    if (exprop(n) == Oucon)
-        return n->expr.args[1];
-    else if (exprop(n) == Olit)
-        return n;
-    else
-        return load(addk(addr(s, n, t), Wordsz));
-}
-
-static void matchpattern(Simp *s, Node *pat, Node *val, Type *t, Node *iftrue, Node *iffalse)
-{
-    Node *n, *v, *x, *y;
-    Node *deeper, *next;
-    Node **patarg, *lit, *idx;
-    char *str;
-    size_t len;
-    Ucon *uc;
-    size_t i;
-    size_t off;
-
-    assert(pat->type == Nexpr);
-    t = tybase(t);
-    if (exprop(pat) == Ovar) {
-        n = decls[pat->expr.did];
-        if (n->decl.isconst) {
-            pat = n->decl.init;
-            if (!pat)
-                die("decl has no init in this file: this is currently unsupported.");
-        } else {
-            v = assign(s, pat, val);
-            append(s, v);
-            jmp(s, iftrue);
-            return;
-        }
-    } else if (exprop(pat) == Ogap) {
-        jmp(s, iftrue);
-        return;
-    }
-    switch (t->type) {
-        /* Never supported */
-        case Tyvoid: case Tybad: case Tyvalist: case Tyvar:
-        case Typaram: case Tyunres: case Tyname: case Ntypes:
-        case Tyfunc: case Tycode:
-            die("Unsupported type for pattern");
-            break;
-        /* only valid for string literals */
-        case Tybool: case Tychar: case Tybyte:
-        case Tyint8: case Tyint16: case Tyint32: case Tyint:
-        case Tyuint8: case Tyuint16: case Tyuint32: case Tyuint:
-        case Tyint64: case Tyuint64:
-        case Tyflt32: case Tyflt64:
-        case Typtr:
-            v = mkexpr(pat->loc, Oeq, pat, val, NULL);
-            v->expr.type = mktype(pat->loc, Tybool);
-            cjmp(s, v, iftrue, iffalse);
-            break;
-        case Tyslice:
-            lit = pat->expr.args[0];
-            if (exprop(pat) != Olit || lit->lit.littype != Lstr)
-                die("Unsupported pattern");
-            str = lit->lit.strval.buf;
-            len = lit->lit.strval.len;
-
-            /* load slice length */
-            next = genlbl(pat->loc);
-            x = slicelen(s, val);
-            y = mkintlit(lit->loc, len);
-            y->expr.type = tyintptr;
-            v = mkexpr(pat->loc, Oeq, x, y, NULL);
-            cjmp(s, v, next, iffalse);
-            append(s, next);
-
-            for (i = 0; i < len; i++) {
-                next = genlbl(pat->loc);
-                x = mkintlit(pat->loc, str[i]);
-                x->expr.type = mktype(pat->loc, Tybyte);
-                idx = mkintlit(pat->loc, i);
-                idx->expr.type = tyintptr;
-                y = load(idxaddr(s, val, idx));
-                v = mkexpr(pat->loc, Oeq, x, y, NULL);
-                v->expr.type = mktype(pat->loc, Tybool);
-                cjmp(s, v, next, iffalse);
-                append(s, next);
-            }
-            jmp(s, iftrue);
-            break;
-        /* We got lucky. The structure of tuple, array, and struct literals
-         * is the same, so long as we don't inspect the type, so we can
-         * share the code*/
-        case Tytuple: case Tyarray: 
-            patarg = pat->expr.args;
-            off = 0;
-            for (i = 0; i < pat->expr.nargs; i++) {
-                off = alignto(off, exprtype(patarg[i]));
-                next = genlbl(pat->loc);
-                v = load(addk(addr(s, val, exprtype(patarg[i])), off));
-                matchpattern(s, patarg[i], v, exprtype(patarg[i]), next, iffalse);
-                append(s, next);
-                off += size(patarg[i]);
-            }
-            jmp(s, iftrue);
-            break;
-        case Tystruct:
-            patarg = pat->expr.args;
-            for (i = 0; i < pat->expr.nargs; i++) {
-                off = offset(pat, patarg[i]->expr.idx);
-                next = genlbl(pat->loc);
-                v = load(addk(addr(s, val, exprtype(patarg[i])), off));
-                matchpattern(s, patarg[i], v, exprtype(patarg[i]), next, iffalse);
-                append(s, next);
-            }
-            break;
-        case Tyunion:
-            if (exprop(pat) == Oucon)
-                uc = finducon(exprtype(pat), pat->expr.args[0]);
-            else
-                uc = finducon(exprtype(val), val->expr.args[0]);
-
-            deeper = genlbl(pat->loc);
-
-            x = uconid(s, pat);
-            y = uconid(s, val);
-            v = mkexpr(pat->loc, Oeq, x, y, NULL);
-            v->expr.type = tyintptr;
-            cjmp(s, v, deeper, iffalse);
-            append(s, deeper);
-            if (uc->etype) {
-                pat = patval(s, pat, uc->etype);
-                val = patval(s, val, uc->etype);
-                matchpattern(s, pat, val, uc->etype, iftrue, iffalse);
-            }
-            break;
-        case Tygeneric:
-            break;
-    }
-}
-
 static void simpblk(Simp *s, Node *n)
 {
     size_t i;
@@ -1727,17 +1586,15 @@
 
 static void simpmatch(Simp *s, Node *n)
 {
-    Node *val, *tmp;
+    Node *val;
     Node **match;
     size_t i, nmatch;
 
-    tmp = temp(s, n->matchstmt.val);
-    val = rval(s, n->matchstmt.val, tmp);
-    append(s, assign(s, tmp, val));
+    val = rval(s, n->matchstmt.val, NULL);
 
     match = NULL;
     nmatch = 0;
-    gensimpmatch(n, tmp, &match, &nmatch);
+    genmatch(n, val, &match, &nmatch);
     for (i = 0; i < nmatch; i++)
         simp(s, match[i]);
 }
--- a/mi/cfg.c
+++ b/mi/cfg.c
@@ -26,13 +26,6 @@
     return bb;
 }
 
-static char *lblstr(Node *n)
-{
-    assert(exprop(n) == Olit);
-    assert(n->expr.args[0]->type == Nlit);
-    assert(n->expr.args[0]->lit.littype == Llbl);
-    return n->expr.args[0]->lit.lblval;
-}
 
 static void strlabel(Cfg *cfg, char *lbl, Bb *bb)
 {
--- a/mi/match.c
+++ b/mi/match.c
@@ -1,4 +1,3 @@
-#include <stdlib.h>
 #include <stdio.h>
 #include <inttypes.h>
 #include <stdarg.h>
@@ -15,34 +14,150 @@
 
 typedef struct Dtree Dtree;
 struct Dtree {
-    /* If the values are equal, go to 'sub'. If 'val' is null, anything matches. */
-    Node *patexpr;      /* the full pattern for this node */
-    Node **val;         /* pattern values to compare against */
-    size_t nval;
-    Node *load;         /* expression value being compared */
-    Dtree **sub;        /* submatch to use if if equal */
-    size_t nsub;
-    Dtree *any;         /* tree for a wildcard match. */
+    int id;
+    Srcloc loc;
 
+    /* values for matching */
+    Node *lbl;
+    Node *load;
+    size_t nconstructors;
+    int accept;
+    int emitted;
+
+    /* the decision tree step */
+    Node **pat;
+    size_t npat;
+    Dtree **next;
+    size_t nnext;
+    Dtree *any;
+
     /* captured variables and action */
     Node **cap;
     size_t ncap;
-    Node *act;
 
-    int id;
 };
 
-void dtdumpnode(Dtree *dt, FILE *f, int depth, int iswild);
-static Dtree *addpat(Dtree *t, Node *pat, Node *val, Node ***cap, size_t *ncap);
-void dtdump(Dtree *dt, FILE *f);
+Dtree *gendtree(Node *m, Node *val, Node **lbl, size_t nlbl);
+static int addpat(Node *pat, Node *val, Dtree *start, Dtree *accept, Node ***cap, size_t *ncap, Dtree ***end, size_t *nend);
+void dtreedump(FILE *fd, Dtree *dt);
 
-/* We treat all integer types, boolean types, etc, as having 2^n constructors.
- *
- * since, of course, we can't represent all of the constructors for 64 bit
- * integers using 64 bit values, we just approximate it. We'd have failed (run
- * out of memory, etc) long before getting to this code if we actually had that
- * many branches of the switch statements anyways.
- */
+
+static Node *utag(Node *n)
+{
+    Node *tag;
+
+    tag = mkexpr(n->loc, Outag, n, NULL);
+    tag->expr.type = mktype(n->loc, Tyint32);
+    return tag;
+}
+
+static Node *uvalue(Node *n, Type *ty)
+{
+    Node *elt;
+
+    elt = mkexpr(n->loc, Oudata, n, NULL);
+    elt->expr.type = ty;
+    return elt;
+}
+
+static Node *tupelt(Node *n, size_t i)
+{
+    Node *idx, *elt;
+
+    idx = mkintlit(n->loc, i);
+    idx->expr.type = mktype(n->loc, Tyuint64);
+    elt = mkexpr(n->loc, Otupget, n, idx, NULL);
+    elt->expr.type = tybase(exprtype(n))->sub[i];
+    return elt;
+}
+
+static Node *arrayelt(Node *n, size_t i)
+{
+    Node *idx, *elt;
+
+    idx = mkintlit(n->loc, i);
+    idx->expr.type = mktype(n->loc, Tyuint64);
+    elt = mkexpr(n->loc, Oidx, n, idx, NULL);
+    elt->expr.type = tybase(exprtype(n))->sub[0];
+    return elt;
+}
+
+static Node *findmemb(Node *pat, Node *name)
+{
+    Node *n;
+    size_t i;
+
+    for (i = 0; i < pat->expr.nargs; i++) {
+        n = pat->expr.args[i];
+        if (nameeq(n->expr.idx, name))
+            return n;
+    }
+    return NULL;
+}
+
+static Dtree *dtbytag(Dtree *t, Ucon *uc)
+{
+    uint32_t tagval;
+    Node *taglit;
+    size_t i;
+
+    for (i = 0; i < t->npat; i++) {
+        taglit = t->pat[i]->expr.args[0];
+        tagval = taglit->lit.intval;
+        if (tagval == uc->id) {
+            return t->next[i];
+        }
+    }
+    return NULL;
+}
+
+static Node *structmemb(Node *n, Node *name, Type *ty)
+{
+    Node *elt;
+
+    elt = mkexpr(n->loc, Omemb, n, name, NULL);
+    elt->expr.type = ty;
+    return elt;
+}
+
+static Node *addcapture(Node *n, Node **cap, size_t ncap)
+{
+    Node **blk;
+    size_t nblk, i;
+
+    nblk = 0;
+    blk = NULL;
+
+    for (i = 0; i < ncap; i++)
+        lappend(&blk, &nblk, cap[i]);
+    for (i = 0; i < n->block.nstmts; i++)
+        lappend(&blk, &nblk, n->block.stmts[i]);
+    lfree(&n->block.stmts, &n->block.nstmts);
+    n->block.stmts = blk;
+    n->block.nstmts = nblk;
+    return n;
+}
+
+static Dtree *mkdtree(Srcloc loc, Node *lbl)
+{
+    static int ndtree;
+    Dtree *t;
+
+    t = zalloc(sizeof(Dtree));
+    t->lbl = lbl;
+    t->loc = loc;
+    t->id = ndtree++;
+    return t;
+}
+
+static Dtree *nextnode(Srcloc loc, size_t idx, size_t count, Dtree *accept)
+{
+    if (idx == count - 1)
+        return accept;
+    else
+        return mkdtree(loc, genlbl(loc));
+}
+
 static size_t nconstructors(Type *t)
 {
     if (!t)
@@ -90,137 +205,211 @@
     return 0;
 }
 
-static int ndt;
-static Dtree *mkdtree()
+static int verifymatch(Dtree *t)
 {
-    Dtree *t;
+    size_t i;
+    int ret;
 
-    t = zalloc(sizeof(Dtree));
-    t->id = ndt++;
-    return t;
-}
+    if (t->accept)
+        return 1;
 
-static Node *tupelt(Node *n, size_t i)
-{
-    Node *idx, *elt;
-
-    idx = mkintlit(n->loc, i);
-    idx->expr.type = mktype(n->loc, Tyuint64);
-    elt = mkexpr(n->loc, Otupget, n, idx, NULL);
-    elt->expr.type = tybase(exprtype(n))->sub[i];
-    return elt;
+    ret = 0;
+    if (t->nnext == t->nconstructors || t->any)
+        ret = 1;
+    for (i = 0; i < t->nnext; i++)
+        if (!verifymatch(t->next[i]))
+            ret = 0;
+    return ret;
 }
 
-static Node *arrayelt(Node *n, size_t i)
+static int acceptall(Dtree *t, Dtree *accept)
 {
-    Node *idx, *elt;
+    size_t i;
+    int ret;
 
-    idx = mkintlit(n->loc, i);
-    idx->expr.type = mktype(n->loc, Tyuint64);
-    elt = mkexpr(n->loc, Oidx, n, idx, NULL);
-    elt->expr.type = tybase(exprtype(n))->sub[0];
-    return elt;
-}
+    if (t->any == accept)
+        return 0;
 
-static Node *structmemb(Node *n, Node *name, Type *ty)
-{
-    Node *elt;
+    ret = 0;
+    if (t->any) {
+        if (acceptall(t->any, accept))
+            ret = 1;
+    } else {
+        t->any = accept;
+        ret = 1;
+    }
 
-    elt = mkexpr(n->loc, Omemb, n, name, NULL);
-    elt->expr.type = ty;
-    return elt;
+    for (i = 0; i < t->nnext; i++)
+        if (acceptall(t->next[i], accept))
+            ret = 1;
+    return ret;
 }
 
-static Node *uvalue(Node *n, Type *ty)
+static int addwildrec(Srcloc loc, Type *ty, Dtree *start, Dtree *accept, Dtree ***end, size_t *nend)
 {
-    Node *elt;
+    Dtree *next, **last, **tail;
+    size_t i, j, nelt, nlast, ntail;
+    Node *asize;
+    Ucon *uc;
+    int ret;
 
-    elt = mkexpr(n->loc, Oudata, n, NULL);
-    elt->expr.type = ty;
-    return elt;
-}
-
-static Node *deadblock()
-{
-    Node *blk, *dead;
-
-    blk = mkblock(Zloc, mkstab(0));
-    dead = mkexpr(Zloc, Odead, NULL);
-    dead->expr.type = mktype(Zloc, Tyvoid);
-    lappend(&blk->block.stmts, &blk->block.nstmts, dead);
-    return blk;
+    tail = NULL;
+    ntail = 0;
+    ty = tybase(ty);
+    if (istyprimitive(ty)) {
+        for (i = 0; i < start->nnext; i++)
+            lappend(end, nend, start->next[i]);
+        if (start->any) {
+            lappend(end, nend, start->any);
+            return 0;
+        } else {
+            start->any = accept;
+            lappend(end, nend, accept);
+            return 1;
+        }
+    }
+    
+    ret = 0;
+    last = NULL;
+    nlast = 0;
+    lappend(&last, &nlast, start);
+    switch (ty->type) {
+        case Tytuple:
+            for (i = 0; i < ty->nsub; i++) {
+                next = nextnode(loc, i, ty->nsub, accept);
+                tail = NULL;
+                ntail = 0;
+                for (j = 0; j < nlast; j++)
+                    if (addwildrec(loc, ty->sub[i], last[j], next, &tail, &ntail))
+                        ret = 1;
+                lfree(&last, &nlast);
+                last = tail;
+                nlast = ntail;
+            }
+            break;
+        case Tyarray:
+            asize = fold(ty->asize, 1);
+            nelt = asize->expr.args[0]->lit.intval;
+            for (i = 0; i < nelt; i++) {
+                next = nextnode(loc, i, nelt, accept);
+                tail = NULL;
+                ntail = 0;
+                for (j = 0; j < nlast; j++)
+                    if (addwildrec(loc, ty->sub[0], last[j], next, &tail, &ntail))
+                        ret = 1;
+                lfree(&last, &nlast);
+                last = tail;
+                nlast = ntail;
+            }
+            break;
+        case Tystruct:
+            for (i = 0; i < ty->nmemb; i++) {
+                next = nextnode(loc, i, ty->nmemb, accept);
+                tail = NULL;
+                ntail = 0;
+                for (j = 0; j < nlast; j++)
+                    if (addwildrec(loc, decltype(ty->sdecls[i]), last[j], next, &tail, &ntail))
+                        ret = 1;
+                lfree(&last, &nlast);
+                last = tail;
+                nlast = ntail;
+            }
+            break;
+        case Tyunion:
+            for (i = 0; i < ty->nmemb; i++) {
+                uc = ty->udecls[i];
+                next = dtbytag(start, uc);
+                if (next) {
+                    if (uc->etype) {
+                        if (addwildrec(loc, uc->etype, next, accept, end, nend))
+                            ret = 1;
+                    } else {
+                        lappend(end, nend, next);
+                    }
+                }
+            }
+            if (!start->any) {
+                start->any = accept;
+                ret = 1;
+            }
+            lappend(end, nend, start->any);
+            break;
+        case Tyslice:
+            ret = acceptall(start, accept);
+            lappend(&last, &nlast, accept);
+            break;
+        default:
+            lappend(&last, &nlast, accept);
+            break;
+    }
+    lcat(end, nend, last, nlast);
+    lfree(&last, &nlast);
+    return ret;
 }
 
-static Dtree *addwild(Dtree *t, Node *pat, Node *val, Node ***cap, size_t *ncap)
+static int addwild(Node *pat, Node *val, Dtree *start, Dtree *accept, Node ***cap, size_t *ncap, Dtree ***end, size_t *nend)
 {
     Node *asn;
 
-    if (t->any)
-        return t->any;
-    t->any = mkdtree();
-    t->any->patexpr = pat;
+    asn = mkexpr(pat->loc, Oasn, pat, val, NULL);
+    asn->expr.type = exprtype(pat);
     if (cap && ncap) {
         asn = mkexpr(pat->loc, Oasn, pat, val, NULL);
         asn->expr.type = exprtype(pat);
         lappend(cap, ncap, asn);
     }
-    return t->any;
+    return addwildrec(pat->loc, exprtype(pat), start, accept, end, nend);
 }
 
-static Dtree *addunion(Dtree *t, Node *pat, Node *val, Node ***cap, size_t *ncap)
+static int addunion(Node *pat, Node *val, Dtree *start, Dtree *accept, Node ***cap, size_t *ncap, Dtree ***end, size_t *nend)
 {
-    Node *elt, *tag, *id;
-    int32_t t1, t2;
-    Dtree *sub;
+    Node *tagid;
+    Dtree *next;
     Ucon *uc;
-    size_t i;
 
-    if (t->any)
-        return t->any;
+    if (start->any) {
+        lappend(end, nend, start->any);
+        return 0;
+    }
+
     uc = finducon(tybase(exprtype(pat)), pat->expr.args[0]);
-    t2 = uc->id;
-    /* if we have the value already... */
-    sub = NULL;
-    for (i = 0; i < t->nval; i++) {
-        tag = t->val[i]->expr.args[0];
-        t1 = tag->lit.intval;
-        if (t1 == t2) {
-            if (pat->expr.nargs > 1) {
-                elt = uvalue(val, exprtype(pat->expr.args[1])); 
-                return addpat(t->sub[i], pat->expr.args[1], elt, cap, ncap);
-            } else {
-                return t->sub[i];
-            }
+    next = dtbytag(start, uc);
+    if (next) {
+        if (!uc->etype) {
+            lappend(end, nend, next);
+            return 0;
+        } else {
+            return addpat(pat->expr.args[1], uvalue(val, uc->etype), next, accept, cap, ncap, end, nend);
         }
     }
 
-    /* otherwise create a new match */
-    sub = mkdtree();
-    sub->patexpr = pat;
-    if (!t->load) {
-        tag = mkexpr(pat->loc, Outag, val, NULL);
-        tag->expr.type = mktype(pat->loc, Tyint32);
-        t->load = tag;
+    if (!start->load) {
+        start->load = utag(val);
+        start->nconstructors = nconstructors(tybase(exprtype(pat)));
     }
 
-    id = mkintlit(pat->loc, uc->id);
-    id->expr.type = mktype(pat->loc, Tyint32);
-
-    lappend(&t->val, &t->nval, id);
-    lappend(&t->sub, &t->nsub, sub);
-    if (pat->expr.nargs == 2) {
-        elt = uvalue(val, exprtype(pat->expr.args[1])); 
-        sub = addpat(sub, pat->expr.args[1], elt, cap, ncap);
+    tagid = mkintlit(pat->loc, uc->id);
+    tagid->expr.type = mktype(pat->loc, Tyint32);
+    lappend(&start->pat, &start->npat, tagid);
+    if (uc->etype) {
+        next = mkdtree(pat->loc, genlbl(pat->loc));
+        lappend(&start->next, &start->nnext, next);
+        addpat(pat->expr.args[1], uvalue(val, uc->etype), next, accept, cap, ncap, end, nend);
+    } else {
+        lappend(&start->next, &start->nnext, accept);
+        lappend(end, nend, accept);
     }
-    return sub;
+    return 1;
 }
 
-static Dtree *addstr(Dtree *t, Node *pat, Node *val, Node ***cap, size_t *ncap)
+static int addstr(Node *pat, Node *val, Dtree *start, Dtree *accept, Node ***cap, size_t *ncap, Dtree ***end, size_t *nend)
 {
+    Dtree **tail, **last, *next;
+    size_t i, j, n, ntail, nlast;
     Node *p, *v, *lit;
-    size_t i, n;
     Type *ty;
     char *s;
+    int ret;
 
     lit = pat->expr.args[0];
     n = lit->lit.strval.len;
@@ -231,140 +420,204 @@
     v = structmemb(val, mkname(pat->loc, "len"), ty);
     p->expr.type = ty;
 
-    t = addpat(t, p, v, cap, ncap);
+    if (n == 0)
+        next = accept;
+    else
+        next = mkdtree(pat->loc, genlbl(pat->loc));
 
+    last = NULL;
+    nlast = 0;
+    if (addpat(p, v, start, next, cap, ncap, &last, &nlast))
+        ret = 1;
+
     ty = mktype(pat->loc, Tybyte);
     for (i = 0; i < n; i++) {
         p = mkintlit(lit->loc, s[i]);
         p->expr.type = ty;
         v = arrayelt(val, i);
-        t = addpat(t, p, v, cap, ncap);
+
+        tail = NULL;
+        ntail = 0;
+        next = nextnode(pat->loc, i, n, accept);
+        for (j = 0; j < nlast; j++) 
+            if (addpat(p, v, last[j], next, NULL, NULL, &tail, &ntail))
+                ret = 1;
+        lfree(&last, &nlast);
+        last = tail;
+        nlast = ntail;
     }
-    return t;
+    lcat(end, nend, last, nlast);
+    lfree(&last, &nlast);
+    return ret;
 }
 
-static Dtree *addlit(Dtree *t, Node *pat, Node *val, Node ***cap, size_t *ncap)
+static int addlit(Node *pat, Node *val, Dtree *start, Dtree *accept, Node ***cap, size_t *ncap, Dtree ***end, size_t *nend)
 {
-    Dtree *sub;
     size_t i;
 
     if (pat->expr.args[0]->lit.littype == Lstr) {
-        sub = addstr(t, pat, val, cap, ncap);
+        return addstr(pat, val, start, accept, cap, ncap, end, nend);
     } else {
-        if (t->any)
-            return t->any;
-        for (i = 0; i < t->nval; i++) {
-            if (liteq(t->val[i]->expr.args[0], pat->expr.args[0]))
-                return t->sub[i];
+        /* if we already have a match, we're not adding a new node */
+        if (start->any) {
+            lappend(end, nend, start->any);
+            return 0;
         }
 
-        sub = mkdtree();
-        sub->patexpr = pat;
-        lappend(&t->val, &t->nval, pat);
-        if (!t->load)
-            t->load = val;
-        lappend(&t->sub, &t->nsub, sub);
+        for (i = 0; i < start->npat; i++) {
+            if (liteq(start->pat[i]->expr.args[0], pat->expr.args[0])) {
+                lappend(end, nend, start->next[i]);
+                return 0;
+            }
+        }
+
+        /* wire up an edge from start to 'accept' */
+        if (!start->load) {
+            start->load = val;
+            start->nconstructors = nconstructors(exprtype(pat));
+        }
+        lappend(&start->pat, &start->npat, pat);
+        lappend(&start->next, &start->nnext, accept);
+        lappend(end, nend, accept);
+        return 1;
     }
-    return sub;
 }
 
-static Dtree *addtup(Dtree *t, Node *pat, Node *val, Node ***cap, size_t *ncap)
+static int addtup(Node *pat, Node *val, Dtree *start, Dtree *accept, Node ***cap, size_t *ncap, Dtree ***end, size_t *nend)
 {
-    size_t i;
-    Node *elt;
+    size_t nargs, nlast, ntail, i, j;
+    Dtree *next, **last, **tail;
+    Node **args;
+    int ret;
 
-    if (t->any)
-        return t->any;
-    for (i = 0; i < pat->expr.nargs; i++) {
-        elt = tupelt(val, i);
-        t = addpat(t, pat->expr.args[i], elt, cap, ncap);
+    args = pat->expr.args;
+    nargs = pat->expr.nargs;
+    last = NULL;
+    nlast = 0;
+    lappend(&last, &nlast, start);
+    ret = 0;
+
+    for (i = 0; i < nargs; i++) {
+        next = nextnode(args[i]->loc, i, nargs, accept);
+        tail = NULL;
+        ntail = 0;
+        for (j = 0; j < nlast; j++) 
+            if (addpat(pat->expr.args[i], tupelt(val, i), last[j], next, cap, ncap, &tail, &ntail))
+                ret = 1;
+        lfree(&last, &nlast);
+        last = tail;
+        nlast = ntail;
     }
-    return t;
+    lcat(end, nend, last, nlast);
+    lfree(&last, &nlast);
+    return ret;
 }
 
-static Dtree *addarr(Dtree *t, Node *pat, Node *val, Node ***cap, size_t *ncap)
+static int addarr(Node *pat, Node *val, Dtree *start, Dtree *accept, Node ***cap, size_t *ncap, Dtree ***end, size_t *nend)
 {
-    size_t i;
-    Node *elt;
+    size_t nargs, nlast, ntail, i, j;
+    Dtree *next, **last, **tail;
+    Node **args;
+    int ret;
 
-    if (t->any)
-        return t->any;
-    for (i = 0; i < pat->expr.nargs; i++) {
-        elt = arrayelt(val, i);
-        t = addpat(t, pat->expr.args[i], elt, cap, ncap);
+    args = pat->expr.args;
+    nargs = pat->expr.nargs;
+    last = NULL;
+    nlast = 0;
+    lappend(&last, &nlast, start);
+    ret = 0;
+
+    for (i = 0; i < nargs; i++) {
+        next = nextnode(args[i]->loc, i, nargs, accept);
+        tail = NULL;
+        ntail = 0;
+        for (j = 0; j < nlast; j++) 
+            if (addpat(pat->expr.args[i], arrayelt(val, i), last[j], next, cap, ncap, &tail, &ntail))
+                ret = 1;
+        lfree(&last, &nlast);
+        last = tail;
+        nlast = ntail;
     }
-    return t;
+    lcat(end, nend, last, nlast);
+    lfree(&last, &nlast);
+    return ret;
 }
 
-static Dtree *addstruct(Dtree *t, Node *pat, Node *val, Node ***cap, size_t *ncap)
+static int addstruct(Node *pat, Node *val, Dtree *start, Dtree *accept, Node ***cap, size_t *ncap, Dtree ***end, size_t *nend)
 {
-    Node *membpat, *membval, *name;
-    char *patname, *membname;
-    size_t i, j;
-    int found;
-    Type *ty;
+    Dtree *next, **last, **tail;
+    Node *memb, *name;
+    Type *ty, *mty;
+    size_t i, j, ntail, nlast;
+    int ret;
 
-    if (t->any)
-        return t->any;
+    ret = 0;
+    last = NULL;
+    nlast = 0;
+    lappend(&last, &nlast, start);
     ty = tybase(exprtype(pat));
+
     for (i = 0; i < ty->nmemb; i++) {
-        found = 0;
+        mty = decltype(ty->sdecls[i]);
         name = ty->sdecls[i]->decl.name;
-        membval = structmemb(val, name, decltype(ty->sdecls[i]));
-        for (j = 0; j < pat->expr.nargs; j++) {
-            membpat = pat->expr.args[j];
-            patname = namestr(membpat->expr.idx);
-            membname = namestr(ty->sdecls[i]->decl.name);
-            if (strcmp(patname, membname) != 0)
-                continue;
-            found = 1;
-            t = addpat(t, membpat, membval, cap, ncap);
+        memb = findmemb(pat, name);
+        next = nextnode(pat->loc, i, ty->nmemb, accept);
+        tail = NULL;
+        ntail = 0;
+
+        for (j = 0; j < nlast; j++) {
+            /* add a _ capture if we don't specify the value */
+            if (!memb) {
+                if (addwild(memb, NULL, last[j], next, NULL, NULL, &tail, &ntail))
+                    ret = 1;
+            } else {
+                if (addpat(memb, structmemb(val, name, mty), last[j], next, cap, ncap, &tail, &ntail))
+                    ret = 1;
+            }
         }
-        if (!found) {
-            membpat = mkexpr(pat->loc, Ogap, NULL);
-            membpat->expr.type = decltype(ty->sdecls[i]);
-            t = addpat(t, membpat, membval, cap, ncap);
-        }
+        lfree(&last, &nlast);
+        last = tail;
+        nlast = ntail;
     }
-    return t;
+    lcat(end, nend, last, nlast);
+    lfree(&last, &nlast);
+    return ret;
 }
 
-static Dtree *addpat(Dtree *t, Node *pat, Node *val, Node ***cap, size_t *ncap)
+static int addpat(Node *pat, Node *val, Dtree *start, Dtree *accept, Node ***cap, size_t *ncap, Dtree ***end, size_t *nend)
 {
-    Dtree *ret;
+    int ret;
     Node *dcl;
 
-    if (pat == NULL)
-        return t;
     pat = fold(pat, 1);
+    ret = 0;
     switch (exprop(pat)) {
         case Ovar:
             dcl = decls[pat->expr.did];
             if (dcl->decl.isconst)
-                ret = addpat(t, dcl->decl.init, val, cap, ncap);
+                ret = addpat(dcl->decl.init, val, start, accept, cap, ncap, end, nend);
             else
-                ret = addwild(t, pat, val, cap, ncap);
+                ret = addwild(pat, val, start, accept, cap, ncap, end, nend);
             break;
         case Oucon:
-            ret = addunion(t, pat, val, cap, ncap);
+            ret = addunion(pat, val, start, accept, cap, ncap, end, nend);
             break;
         case Olit:
-            ret = addlit(t, pat, val, cap, ncap);
+            ret = addlit(pat, val, start, accept, cap, ncap, end, nend);
             break;
         case Otup:
-            ret = addtup(t, pat, val, cap, ncap);
+            ret = addtup(pat, val, start, accept, cap, ncap, end, nend);
             break;
         case Oarr:
-            ret = addarr(t, pat, val, cap, ncap);
+            ret = addarr(pat, val, start, accept, cap, ncap, end, nend);
             break;
         case Ostruct:
-            ret = addstruct(t, pat, val, cap, ncap);
+            ret = addstruct(pat, val, start, accept, cap, ncap, end, nend);
             break;
         case Ogap:
-            ret = addwild(t, pat, val, NULL, NULL);
+            ret = addwild(pat, NULL, start, accept, NULL, NULL, end, nend);
             break;
         default:
-            ret = NULL;
             fatal(pat, "unsupported pattern %s of type %s", opstr[exprop(pat)], tystr(exprtype(pat)));
             break;
     }
@@ -371,202 +624,166 @@
     return ret;
 }
 
-static int isexhaustive(Dtree *dt)
+
+/* val must be a pure, fully evaluated value */
+Dtree *gendtree(Node *m, Node *val, Node **lbl, size_t nlbl)
 {
-    Type *subt;
+    Dtree *start, *accept, **end;
+    Node **pat, **cap;
+    size_t npat, ncap, nend;
     size_t i;
 
-    if (dt->any)
-        return 1;
+    pat = m->matchstmt.matches;
+    npat = m->matchstmt.nmatches;
 
-    if (dt->nsub > 0) {
-        subt = tybase(exprtype(dt->sub[0]->patexpr));
-        if (dt->nsub != nconstructors(subt))
-            return 0;
+    end = NULL;
+    nend = 0;
+    start = mkdtree(m->loc, genlbl(m->loc));
+    for (i = 0; i < npat; i++) {
+        cap = NULL;
+        ncap = 0;
+        accept = mkdtree(lbl[i]->loc, lbl[i]);
+        accept->accept = 1;
+
+        if (!addpat(pat[i]->match.pat, val, start, accept, &cap, &ncap, &end, &nend))
+            fatal(pat[i], "pattern matched by earlier case");
+        addcapture(pat[i]->match.block, cap, ncap);
     }
-    switch (exprop(dt->patexpr)) {
-        case Ovar:
-            return 1;
-        case Olit:
-            return 1;
-        case Oucon:
-            for (i = 0; i < dt->nsub; i++)
-                if (!isexhaustive(dt->sub[i]))
-                    return 0;
-            return 1;
-            break;
-        case Otup:
-            for (i = 0; i < dt->nsub; i++)
-                if (!isexhaustive(dt->sub[i]))
-                    return 0;
-            return 1;
-            break;
-        case Oarr:
-            for (i = 0; i < dt->nsub; i++)
-                if (!isexhaustive(dt->sub[i]))
-                    return 0;
-            return 1;
-            break;
-        case Ostruct:
-            for (i = 0; i < dt->nsub; i++)
-                if (!isexhaustive(dt->sub[i]))
-                    return 0;
-            return 1;
-            break;
-        default:
-            die("Invalid pattern in exhaustivenes check. BUG.");
-            break;
-    }
-    return 0;
+    if (!verifymatch(start))
+        fatal(m, "nonexhaustive pattern set in match statement");
+    return start;
 }
 
-static int exhaustivematch(Node *m, Dtree *t, Type *tt)
+void genmatchcode(Dtree *dt, Node ***out, size_t *nout)
 {
+    Node *jmp, *eq, *fail;
+    int emit;
     size_t i;
 
-    if (t->any)
-        return 1;
-    if (t->nsub != nconstructors(tt))
-        return 0;
-    for (i = 0; i < t->nsub; i++)
-        if (!isexhaustive(t->sub[i]))
-            return 0;
-    return 1;
-}
+    if (dt->emitted)
+        return;
+    dt->emitted = 1;
 
-Node *addcapture(Dtree *dt, Node *n)
-{
-    Node **blk;
-    size_t nblk, i;
+    /* the we jump to the accept label when generating the parent */
+    if (dt->accept) {
+        jmp = mkexpr(dt->loc, Ojmp, dt->lbl, NULL);
+        jmp->expr.type = mktype(dt->loc, Tyvoid);
+        lappend(out, nout, jmp);
+        return;
+    }
 
-    nblk = 0;
-    blk = NULL;
+    lappend(out, nout, dt->lbl);
+    for (i = 0; i < dt->nnext; i++) {
+        if (i == dt->nnext - 1 && dt->any) {
+            fail = dt->any->lbl;
+            emit = 0;
+        } else {
+            fail = genlbl(dt->loc);
+            emit = 1;
+        }
 
-    for (i = 0; i < dt->ncap; i++)
-        lappend(&blk, &nblk, dt->cap[i]);
-    for (i = 0; i < n->block.nstmts; i++)
-        lappend(&blk, &nblk, n->block.stmts[i]);
-    lfree(&n->block.stmts, &n->block.nstmts);
-    n->block.stmts = blk;
-    n->block.nstmts = nblk;
-    return n;
+        eq = mkexpr(dt->loc, Oeq, dt->load, dt->pat[i], NULL);
+        eq->expr.type = mktype(dt->loc, Tybool);
+        jmp = mkexpr(dt->loc, Ocjmp, eq, dt->next[i]->lbl, fail, NULL);
+        jmp->expr.type = mktype(dt->loc, Tyvoid);
+        lappend(out, nout, jmp);
+
+        genmatchcode(dt->next[i], out, nout);
+        if (emit)
+            lappend(out, nout, fail);
+    }
+    if (dt->any)
+        genmatchcode(dt->any, out, nout);
 }
 
-static Node *genmatch(Srcloc loc, Dtree *dt, Node *lastany)
+void genonematch(Node *pat, Node *val, Node *iftrue, Node *iffalse, Node ***out, size_t *nout, Node ***cap, size_t *ncap)
 {
-    Node *lastcmp, *cmp, *eq, *pat, *any;
-    size_t i;
+    Dtree *start, *accept, *reject, **end;
+    size_t nend;
 
-    lastcmp = NULL;
-    cmp = NULL;
-    pat = NULL;
-    /* we must have an action if this is a terminal leaf */
-    if (dt->any && dt->any->act)
-        lastany = dt->any->act;
-    if (dt->nsub == 0 && !dt->any)
-        return addcapture(dt, dt->act);
-    for (i = 0; i < dt->nsub; i++) {
-        eq = mkexpr(loc, Oeq, dt->load, dt->val[i], NULL);
-        cmp = mkifstmt(loc, eq, genmatch(loc, dt->sub[i], lastany), NULL);
-        if (!pat)
-            pat = cmp;
-        if (!lastcmp)
-            lastcmp = cmp;
-        else
-            lastcmp->ifstmt.iffalse = cmp;
-        lastcmp = cmp;
-    }
-    if (dt->any) {
-        any = genmatch(loc, dt->any, lastany);
-        if (lastcmp)
-            lastcmp->ifstmt.iffalse = any;
-        else
-            pat = any;
-    } else if (lastcmp) {
-        lastcmp->ifstmt.iffalse = lastany;
-    }
+    end = NULL;
+    nend = 0;
 
-    return pat;
+    start = mkdtree(pat->loc, genlbl(pat->loc));
+    accept = mkdtree(iftrue->loc, iftrue);
+    accept->accept = 1;
+    reject = mkdtree(iffalse->loc, iffalse);
+    reject->accept = 1;
+
+    assert(addpat(pat, val, start, accept, cap, ncap, &end, &nend));
+    acceptall(start, reject);
+    genmatchcode(start, out, nout);
 }
 
-/* val must be a pure, fully evaluated value */
-void gensimpmatch(Node *m, Node *val, Node ***out, size_t *nout)
+void genmatch(Node *m, Node *val, Node ***out, size_t *nout)
 {
-    Node **pat, **cap;
-    size_t npat, ncap;
-    Dtree *t, *leaf;
-    size_t i;
-    Node *n;
+    Node **pat, **lbl, *end, *endlbl;
+    size_t npat, nlbl, i;
+    Dtree *dt;
 
+    lbl = NULL;
+    nlbl = 0;
+
     pat = m->matchstmt.matches;
     npat = m->matchstmt.nmatches;
-    t = mkdtree();
+    for (i = 0; i < npat; i++)
+        lappend(&lbl, &nlbl, genlbl(pat[i]->match.block->loc));
+
+
+    endlbl = genlbl(m->loc);
+    dt = gendtree(m, val, lbl, nlbl);
+    genmatchcode(dt, out, nout);
+
     for (i = 0; i < npat; i++) {
-        cap = NULL;
-        ncap = 0;
-        leaf = addpat(t, pat[i]->match.pat, val, &cap, &ncap);
-        if (leaf->act)
-            fatal(pat[i], "pattern matched by earlier case on line %d", leaf->act->loc.line);
-        leaf->act = pat[i]->match.block;
-        leaf->cap = cap;
-        leaf->ncap = ncap;
+        end = mkexpr(pat[i]->loc, Ojmp, endlbl, NULL);
+        end->expr.type = mktype(end->loc, Tyvoid);
+        lappend(out, nout, lbl[i]);
+        lappend(out, nout, pat[i]->match.block);
+        lappend(out, nout, end);
     }
-    if (!exhaustivematch(m, t, exprtype(m->matchstmt.val)))
-        fatal(m, "nonexhaustive pattern set in match statement");
-    n = genmatch(m->loc, t, deadblock());
-    lappend(out, nout, n);
+    lappend(out, nout, endlbl);
+    
+    //dtreedump(stdout, dt);
+    //for (i = 0; i < *nout; i++)
+    //    dump((*out)[i], stdout);
 }
 
-char *dtnodestr(Node *n)
+
+void dtreedumplit(FILE *fd, Dtree *dt, Node *n, size_t depth)
 {
-    switch (exprop(n)) {
-        case Ovar:
-            return namestr(n->expr.args[0]);
-        case Olit:
-            return litstr[n->expr.args[0]->lit.littype];
-        case Oucon:
-            return namestr(n->expr.args[0]);
-        case Otup:
-            return "tuple";
-        case Oarr:
-            return "array";
-        case Ostruct:
-            return "struct";
-        case Ogap:
-            return "_";
-        case Oasn:
-            return dtnodestr(n->expr.args[0]);
-        default:
-            die("Invalid pattern in exhaustivenes check. BUG.");
-            break;
+    char *s;
+
+    s = lblstr(dt->lbl);
+    switch (n->lit.littype) {
+        case Lchr:      findentf(fd, depth, "%s: Lchr %c\n", s, n->lit.chrval); break;
+        case Lbool:     findentf(fd, depth, "%s: Lbool %s\n", s, n->lit.boolval ? "true" : "false"); break;
+        case Lint:      findentf(fd, depth, "%s: Lint %llu\n", s, n->lit.intval); break;
+        case Lflt:      findentf(fd, depth, "%s: Lflt %lf\n", s, n->lit.fltval); break;
+        case Lstr:      findentf(fd, depth, "%s: Lstr %.*s\n", s, (int)n->lit.strval.len, n->lit.strval.buf); break;
+        case Llbl:      findentf(fd, depth, "%s: Llbl %s\n", s, n->lit.lblval); break;
+        case Lfunc:     findentf(fd, depth, "%s: Lfunc\n"); break;
     }
-    return "???";
 }
 
-void dtdumpnode(Dtree *dt, FILE *f, int depth, int iswild)
+void dtreedumpnode(FILE *fd, Dtree *dt, size_t depth)
 {
-    Node *e;
     size_t i;
-    char *s;
 
-    if (dt->patexpr) {
-        e = dt->patexpr;
-        s = tystr(exprtype(e));
-        indentf(depth, "%s%s %s : %s\n", iswild ? "WILDCARD " : "", opstr[exprop(e)], dtnodestr(e), s);
-        free(s);
-    } 
-    if (dt->cap)
-        for (i = 0; i < dt->ncap; i++)
-            indentf(depth + 1, "capture %s\n", dtnodestr(dt->cap[i]));
-    if (dt->act)
-        indentf(depth + 1, "action\n");
-    for (i = 0; i < dt->nsub; i++)
-        dtdumpnode(dt->sub[i], f, depth + 1, 0);
-    if (dt->any)
-        dtdumpnode(dt->any, f, depth + 1, 1);
+    if (dt->accept)
+        findentf(fd, depth, "%s: accept\n", lblstr(dt->lbl));
+
+    for (i = 0; i < dt->nnext; i++) {
+        dtreedumplit(fd, dt, dt->pat[i]->expr.args[0], depth);
+        dtreedumpnode(fd, dt->next[i], depth + 1);
+    }
+    if (dt->any) {
+        findentf(fd, depth, "%s: wildcard\n", lblstr(dt->lbl));
+        dtreedumpnode(fd, dt->any, depth + 1);
+    }
 }
 
-void dtdump(Dtree *dt, FILE *f)
+void dtreedump(FILE *fd, Dtree *dt)
 {
-    dtdumpnode(dt, f, 0, 0);
+    dtreedumpnode(fd, dt, 0);
 }
+
--- a/mi/mi.h
+++ b/mi/mi.h
@@ -48,4 +48,5 @@
 void check(Cfg *cfg);
 
 /* pattern matching */
-void gensimpmatch(Node *m, Node *val, Node ***out, size_t *nout);
+void genmatch(Node *m, Node *val, Node ***out, size_t *nout);
+void genonematch(Node *pat, Node *val, Node *iftrue, Node *iffalse, Node ***out, size_t *nout, Node ***cap, size_t *ncap);
--- a/parse/node.c
+++ b/parse/node.c
@@ -260,7 +260,6 @@
     return mklbl(loc, buf);
 }
 
-
 Node *mkstr(Srcloc loc, Str val)
 {
     Node *n;
@@ -483,6 +482,14 @@
         return "";
     assert(name->type == Nname);
     return name->name.name;
+}
+
+char *lblstr(Node *n)
+{
+    assert(exprop(n) == Olit);
+    assert(n->expr.args[0]->type == Nlit);
+    assert(n->expr.args[0]->lit.littype == Llbl);
+    return n->expr.args[0]->lit.lblval;
 }
 
 static size_t did(Node *n)
--- a/parse/parse.h
+++ b/parse/parse.h
@@ -568,6 +568,7 @@
 Node *mkname(Srcloc l, char *name);
 Node *mknsname(Srcloc l, char *ns, char *name);
 Node *mkdecl(Srcloc l, Node *name, Type *ty);
+Node *gentemp(Srcloc loc, Type *ty, Node **dcl);
 Node *mklbl(Srcloc l, char *lbl);
 Node *genlbl(Srcloc loc);
 char *genlblstr(char *buf, size_t sz, char *suffix);
@@ -577,6 +578,7 @@
 /* node util functions */
 uint64_t arraysz(Node *sz);
 char *namestr(Node *name);
+char *lblstr(Node *n);
 char *declname(Node *n);
 Type *decltype(Node *n);
 Type *exprtype(Node *n);
@@ -617,6 +619,7 @@
 
 /* convenience funcs */
 void lappend(void *l, size_t *len, void *n); /* hack; nl is void* b/c void*** is incompatible with T*** */
+void lcat(void *dst, size_t *ndst, void *src, size_t nsrc);
 void linsert(void *l, size_t *len, size_t idx, void *n);
 void *lpop(void *l, size_t *len);
 void ldel(void *l, size_t *len, size_t idx);
--- a/parse/util.c
+++ b/parse/util.c
@@ -170,6 +170,17 @@
     *pl = xrealloc(l, *len * sizeof(void*));
 }
 
+void lcat(void *dst, size_t *ndst, void *src, size_t nsrc)
+{
+    size_t i;
+    void ***d, **s;
+
+    d = dst;
+    s = src;
+    for (i = 0; i < nsrc; i++)
+        lappend(d, ndst, s[i]);
+}
+
 
 void lfree(void *l, size_t *len)
 {