ref: f6e53709407611de940092c9d73e63926723252f
author: Ori Bernstein <[email protected]>
date: Tue Jun 17 21:10:30 EDT 2014
Merge pull request #1 from akoshibe/master added sockproto values
--- /dev/null
+++ b/6/Makefile
@@ -1,0 +1,11 @@
+INSTBIN=6m
+BIN=6m
+OBJ=isel.o \
+ locs.o \
+ main.o \
+ ra.o \
+ simp.o \
+
+DEPS=../parse/libparse.a ../mi/libmi.a
+
+include ../mk/c.mk
--- /dev/null
+++ b/6/asm.h
@@ -1,0 +1,234 @@
+#define Maxarg 4 /* maximum number of args an insn can have */
+#define Maxuse (2*Maxarg) /* maximum number of registers an insn can use or def */
+#define Maxdef (2*Maxarg) /* maximum number of registers an insn can use or def */
+#define Wordsz 4 /* the size of a "natural int" */
+#define Ptrsz 8 /* the size of a machine word (ie, pointer size) */
+#define Nsaved 13 /* number of registers saved in the ABI */
+
+typedef size_t regid;
+
+typedef struct Insn Insn;
+typedef struct Loc Loc;
+typedef struct Func Func;
+typedef struct Blob Blob;
+typedef struct Isel Isel;
+typedef struct Asmbb Asmbb;
+
+typedef enum {
+#define Insn(val, fmt, use, def) val,
+#include "insns.def"
+#undef Insn
+} AsmOp;
+
+typedef enum {
+#define Reg(r, name, mode) r,
+#include "regs.def"
+#undef Reg
+ Nreg
+} Reg;
+
+typedef enum {
+ Locnone,
+ Loclbl, /* label */
+ Locreg, /* register */
+ Locmem, /* reg offset mem */
+ Locmeml, /* label offset mem */
+ Loclit, /* literal value */
+ Loclitl /* label address */
+} Loctype;
+
+typedef enum {
+ ModeNone,
+ ModeB, /* byte */
+ ModeW, /* short */
+ ModeL, /* long */
+ ModeQ, /* quad */
+ ModeF, /* float32 */
+ ModeD, /* float64 */
+ Nmode,
+} Mode;
+
+typedef enum {
+ Classbad,
+ Classint,
+ Classflt,
+ Nclass,
+} Rclass;
+
+/* a register, label, or memory location */
+struct Loc {
+ Loctype type; /* the type of loc */
+ Mode mode; /* the mode of this location */
+ void *list;
+ union {
+ char *lbl; /* for Loclbl, Loclitl */
+ struct { /* for Locreg */
+ regid id;
+ Reg colour;
+ } reg;
+ long lit; /* for Loclit */
+ /*
+ * for Locmem, Locmeml.
+ * address format is
+ * disp(base + index)
+ */
+ struct {
+ /* only one of lbldisp and constdisp may be used */
+ char *lbldisp;
+ long constdisp;
+ int scale; /* 0,1,2,4, or 8 */
+ Loc *base; /* needed */
+ Loc *idx; /* optional */
+ } mem;
+ };
+};
+
+struct Insn {
+ AsmOp op;
+ Loc *args[Maxarg];
+ size_t nargs;
+};
+
+struct Func {
+ char *name; /* function name */
+ int isexport; /* is this exported from the asm? */
+ size_t stksz; /* stack size */
+ Type *type; /* type of function */
+ Htab *stkoff; /* Loc* -> int stackoff map */
+ Node *ret; /* return value */
+ Cfg *cfg; /* flow graph */
+};
+
+struct Asmbb {
+ int id; /* unique identifier */
+ char **lbls; /* list of BB labels */
+ size_t nlbls; /* number of labels */
+ Insn **il; /* instructions */
+ size_t ni; /* number of instructions */
+
+ Bitset *pred; /* set of predecessor BB ids */
+ Bitset *succ; /* set of successor BB ids */
+ Bitset *use; /* registers used by this BB */
+ Bitset *def; /* registers defined by this BB */
+ Bitset *livein; /* variables live on entrance to BB */
+ Bitset *liveout; /* variables live on exit from BB */
+};
+
+/* instruction selection state */
+struct Isel {
+ Cfg *cfg; /* cfg built with nodes */
+
+ Asmbb **bb; /* 1:1 mappings with the Node bbs in the CFG */
+ size_t nbb;
+ Asmbb *curbb;
+
+ Node *ret; /* we store the return into here */
+ Htab *spillslots; /* reg id => int stkoff */
+ Htab *reglocs; /* decl id => Loc *reg */
+ Htab *stkoff; /* decl id => int stkoff */
+ Htab *globls; /* decl id => char *globlname */
+
+ /* increased when we spill */
+ Loc *stksz;
+ Loc *calleesave[Nsaved];
+
+ /* register allocator state */
+
+ size_t *gbits; /* igraph matrix repr */
+ regid **gadj; /* igraph adj set repr */
+ size_t *ngadj;
+ int *degree; /* degree of nodes */
+ Loc **aliasmap; /* mapping of aliases */
+
+ Bitset *shouldspill; /* the first registers we should try to spill */
+ Bitset *neverspill; /* registers we should never spill */
+
+ Insn ***rmoves;
+ size_t *nrmoves;
+
+ /* move sets */
+ Insn **mcoalesced;
+ size_t nmcoalesced;
+
+ Insn **mconstrained;
+ size_t nmconstrained;
+
+ Insn **mfrozen;
+ size_t nmfrozen;
+
+ Insn **mactive;
+ size_t nmactive;
+
+
+ /* worklists */
+ Insn **wlmove;
+ size_t nwlmove;
+
+ Loc **wlspill;
+ size_t nwlspill;
+
+ Loc **wlfreeze;
+ size_t nwlfreeze;
+
+ Loc **wlsimp;
+ size_t nwlsimp;
+
+ Loc **selstk;
+ size_t nselstk;
+
+ Bitset *coalesced;
+ Bitset *spilled;
+ Bitset *prepainted; /* locations that need to be a specific colour */
+ Bitset *initial; /* initial set of locations used by this fn */
+};
+
+/* entry points */
+void genblob(FILE *fd, Node *blob, Htab *globls, Htab *strtab);
+void genasm(FILE *fd, Func *fn, Htab *globls, Htab *strtab);
+void genstrings(FILE *fd, Htab *strtab);
+void gen(Node *file, char *out);
+
+/* location generation */
+extern size_t maxregid;
+extern Loc **locmap; /* mapping from reg id => Loc * */
+
+char *genlblstr(char *buf, size_t sz);
+Node *genlbl(void);
+Loc *loclbl(Node *lbl);
+Loc *locstrlbl(char *lbl);
+Loc *locreg(Mode m);
+Loc *locphysreg(Reg r);
+Loc *locmem(long disp, Loc *base, Loc *idx, Mode mode);
+Loc *locmeml(char *disp, Loc *base, Loc *idx, Mode mode);
+Loc *locmems(long disp, Loc *base, Loc *idx, int scale, Mode mode);
+Loc *locmemls(char *disp, Loc *base, Loc *idx, int scale, Mode mode);
+Loc *loclit(long val, Mode m);
+Loc *loclitl(char *lbl);
+Loc *coreg(Reg r, Mode m);
+
+void locprint(FILE *fd, Loc *l, char spec);
+void iprintf(FILE *fd, Insn *insn);
+
+/* emitting instructions */
+Insn *mkinsn(AsmOp op, ...);
+
+/* register allocation */
+extern char *regnames[]; /* name table */
+extern Mode regmodes[]; /* mode table */
+extern size_t modesize[]; /* mode size table */
+void regalloc(Isel *s);
+Rclass rclass(Loc *l);
+
+
+/* useful functions */
+size_t tysize(Type *t);
+size_t size(Node *n);
+int stacktype(Type *t);
+int floattype(Type *t);
+int stacknode(Node *n);
+int floatnode(Node *n);
+void breakhere();
+void dumpasm(Isel *s, FILE *fd);
+
+size_t alignto(size_t sz, Type *t);
+
--- /dev/null
+++ b/6/insns.def
@@ -1,0 +1,96 @@
+/* Table of instructions. Each instruction
+ is defined by the following macro:
+ Insn(enumval, fmt, attr)
+ The format string 'fmt' has the following expansions:
+ %r - int reg
+ %f - xmm reg
+ %m - mem
+ %i - imm
+ %v - reg/mem
+ %u - reg/imm
+ %x - reg/freg/mem/imm
+ %[1-9]*t - Mode of an operand. The optional number
+ preceeding it is the operand desired for
+ the mode.
+ Currently, there aren't any attrs, because none were needed yet.
+ Eventually, they'll probably include flag setting and so on.
+
+ For technical reasons, the indexing on use and def statments is 1-based,
+ instead of 0-based. (0 is the sentinel value).
+*/
+
+Insn(Inone, "BAD_INSN", Use(), Def())
+/* Note, the mov instruction is specified in an overly general manner. */
+Insn(Imov, "\tmov%t %x,%x\n", Use(.l={1}), Def(.l={2}))
+Insn(Imovt, "PSEUDO: TRUNCATE\n", Use(.l={1}), Def(.l={2}))
+Insn(Imovzx, "\tmovz%1t%2t %x,%x\n", Use(.l={1}), Def(.l={2}))
+Insn(Imovsx, "\tmovs%1t%2t %x,%x\n", Use(.l={1}), Def(.l={2}))
+Insn(Irepmovsb, "\trep movsb\n", Use(.r={Rrcx,Rrsi,Rrdi}), Def())
+Insn(Irepmovsw, "\trep movsw\n", Use(.r={Rrcx,Rrsi,Rrdi}), Def())
+Insn(Irepmovsl, "\trep movsl\n", Use(.r={Rrcx,Rrsi,Rrdi}), Def())
+Insn(Irepmovsq, "\trep movsq\n", Use(.r={Rrcx,Rrsi,Rrdi}), Def())
+Insn(Ilea, "\tlea%2t %m,%r\n", Use(.l={1}), Def(.l={2}))
+
+Insn(Iadd, "\tadd%t %x,%r\n", Use(.l={1,2}), Def(.l={2}))
+Insn(Isub, "\tsub%t %x,%r\n", Use(.l={1,2}), Def(.l={2}))
+Insn(Iimul, "\timul%t %x,%r\n", Use(.l={1,2}), Def(.l={2}))
+/* there is no imul for 8 bit values. */
+Insn(Iimul_r, "\timul%t %r\n", Use(.l={1},.r={Ral}), Def(.r={Rax}))
+Insn(Imul, "\tmul%t %r\n", Use(.l={1},.r={Reax}), Def(.r={Reax,Redx}))
+Insn(Idiv, "\tdiv%t %r\n", Use(.l={1},.r={Reax,Redx}), Def(.r={Reax,Redx}))
+Insn(Ineg, "\tneg%t %r\n", Use(.l={1}), Def(.l={1}))
+Insn(Iand, "\tand%t %x,%r\n", Use(.l={1,2}), Def(.l={2}))
+Insn(Ior, "\tor%t %x,%r\n", Use(.l={1,2}), Def(.l={2}))
+Insn(Ixor, "\txor%t %x,%r\n", Use(.l={1,2}), Def(.l={2}))
+Insn(Inot, "\tnot%t %v\n", Use(.l={1}), Def(.l={1}))
+Insn(Ishl, "\tsal%2t %u,%r\n", Use(.l={1,2}), Def(.l={2}))
+Insn(Isar, "\tsar%2t %u,%r\n", Use(.l={1,2}), Def(.l={2}))
+Insn(Ishr, "\tshr%2t %u,%r\n", Use(.l={1,2}), Def(.l={2}))
+
+Insn(Itest, "\ttest%t %x,%r\n", Use(.l={1,2}), Def(.l={}))
+Insn(Icmp, "\tcmp%t %x,%r\n", Use(.l={1,2}), Def(.l={}))
+
+Insn(Ipush, "\tpush%t %r\n", Use(.l={1}), Def())
+Insn(Ipop, "\tpop%t %r\n", Use(.l={1}), Def())
+
+/* branch instructions */
+Insn(Isetz, "\tsetz %v\n", Use(), Def(.l={1}))
+Insn(Isetnz, "\tsetnz %v\n", Use(), Def(.l={1}))
+Insn(Isetl, "\tsetl %v\n", Use(), Def(.l={1}))
+Insn(Isetle, "\tsetle %v\n", Use(), Def(.l={1}))
+Insn(Isetg, "\tsetg %v\n", Use(), Def(.l={1}))
+Insn(Isetge, "\tsetge %v\n", Use(), Def(.l={1}))
+Insn(Isetb, "\tsetb %v\n", Use(), Def(.l={1}))
+Insn(Isetbe, "\tsetbe %v\n", Use(), Def(.l={1}))
+Insn(Iseta, "\tseta %v\n", Use(), Def(.l={1}))
+Insn(Isetae, "\tsetae %v\n", Use(), Def(.l={1}))
+
+/* fp specific instructions */
+Insn(Imovs, "\tmovs%1t %x,%x\n", Use(.l={1}), Def(.l={2}))
+Insn(Icvttsd2si, "\tcvttsd2si%2t %x,%r\n", Use(.l={1}), Def(.l={2}))
+Insn(Icvttsi2sd, "\tcvttsi2sd%2t %x,%f\n", Use(.l={1}), Def(.l={2}))
+Insn(Iadds, "\tadds%t %x,%f\n", Use(.l={1,2}), Def(.l={2}))
+Insn(Isubs, "\tsubs%t %x,%f\n", Use(.l={1,2}), Def(.l={2}))
+Insn(Imuls, "\tmuls%t %x,%f\n", Use(.l={1,2}), Def(.l={2}))
+Insn(Idivs, "\tdivs%t %x,%f\n", Use(.l={1,2}), Def(.l={2}))
+Insn(Icomis, "\tcomis%t %x,%f\n", Use(.l={1,2}), Def())
+Insn(Ixorp, "\tmuls%t %x,%f\n", Use(.l={1,2}), Def(.l={2}))
+
+/* branch instructions */
+Insn(Icall, "\tcall %v\n", Use(.l={1}), Def(.r={Rrax,Reax,Rax,Ral,Rah}))
+Insn(Icallind, "\tcall *%v\n", Use(.l={1}), Def(.r={Rrax,Reax,Rax,Ral,Rah}))
+Insn(Ijmp, "\tjmp %v\n", Use(.l={1}), Def())
+Insn(Ijz, "\tjz %v\n", Use(.l={1}), Def())
+Insn(Ijnz, "\tjnz %v\n", Use(.l={1}), Def())
+Insn(Ijl, "\tjl %v\n", Use(.l={1}), Def())
+Insn(Ijle, "\tjle %v\n", Use(.l={1}), Def())
+Insn(Ijg, "\tjg %v\n", Use(.l={1}), Def())
+Insn(Ijge, "\tjge %v\n", Use(.l={1}), Def())
+Insn(Ijb, "\tjb %v\n", Use(.l={1}), Def())
+Insn(Ijbe, "\tjbe %v\n", Use(.l={1}), Def())
+Insn(Ija, "\tja %v\n", Use(.l={1}), Def())
+Insn(Ijae, "\tjae %v\n", Use(.l={1}), Def())
+Insn(Iret, "\tret\n", Use(), Def())
+
+/* not really an insn... */
+Insn(Ilbl, "%v:\n", Use(), Def())
--- /dev/null
+++ b/6/isel.c
@@ -1,0 +1,1221 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <inttypes.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 "parse.h"
+#include "opt.h"
+#include "asm.h"
+
+#include "platform.h"
+
+/* string tables */
+char *insnfmts[] = {
+#define Insn(val, fmt, use, def) fmt,
+#include "insns.def"
+#undef Insn
+};
+
+char* modenames[] = {
+ [ModeB] = "b",
+ [ModeW] = "w",
+ [ModeL] = "l",
+ [ModeQ] = "q",
+ [ModeF] = "s",
+ [ModeD] = "d"
+};
+
+/* forward decls */
+Loc *selexpr(Isel *s, Node *n);
+static size_t writeblob(FILE *fd, Htab *globls, Htab *strtab, Node *blob);
+
+/* used to decide which operator is appropriate
+ * for implementing various conditional operators */
+struct {
+ AsmOp test;
+ AsmOp jmp;
+ AsmOp getflag;
+} reloptab[Numops] = {
+ [Olnot] = {Itest, Ijz, Isetz}, /* lnot invalid for floats */
+ /* signed int */
+ [Oeq] = {Icmp, Ijz, Isetz},
+ [One] = {Icmp, Ijnz, Isetnz},
+ [Ogt] = {Icmp, Ijg, Isetg},
+ [Oge] = {Icmp, Ijge, Isetge},
+ [Olt] = {Icmp, Ijl, Isetl},
+ [Ole] = {Icmp, Ijle, Isetle},
+ /* unsigned int */
+ [Oueq] = {Icmp, Ijz, Isetz},
+ [Oune] = {Icmp, Ijnz, Isetnz},
+ [Ougt] = {Icmp, Ija, Iseta},
+ [Ouge] = {Icmp, Ijae, Isetae},
+ [Oult] = {Icmp, Ijb, Isetb},
+ [Oule] = {Icmp, Ijbe, Isetbe},
+ /* float */
+ [Ofeq] = {Icomis, Ijz, Isetz},
+ [Ofne] = {Icomis, Ijnz, Isetnz},
+ [Ofgt] = {Icomis, Ija, Iseta},
+ [Ofge] = {Icomis, Ijae, Isetae},
+ [Oflt] = {Icomis, Ijb, Isetb},
+ [Ofle] = {Icomis, Ijbe, Isetbe},
+};
+
+static Mode mode(Node *n)
+{
+ Type *t;
+
+ t = tybase(exprtype(n));
+ /* FIXME: What should the mode for, say, structs be when we have no
+ * intention of loading /through/ the pointer? For now, we'll just say it's
+ * the pointer mode, since we expect to address through the pointer */
+ switch (t->type) {
+ case Tyfloat32: return ModeF; break;
+ case Tyfloat64: return ModeD; break;
+ default:
+ if (stacktype(t))
+ return ModeQ;
+ switch (size(n)) {
+ case 1: return ModeB; break;
+ case 2: return ModeW; break;
+ case 4: return ModeL; break;
+ case 8: return ModeQ; break;
+ }
+ break;
+ }
+ return ModeQ;
+}
+
+static int isintmode(Mode m)
+{
+ return m == ModeB || m == ModeW || m == ModeL || m == ModeQ;
+}
+
+static int isfloatmode(Mode m)
+{
+ return m == ModeF || m == ModeD;
+}
+
+static Loc *loc(Isel *s, Node *n)
+{
+ ssize_t stkoff;
+ Loc *l, *rip;
+ Node *v;
+
+ switch (exprop(n)) {
+ case Ovar:
+ if (hthas(s->globls, n)) {
+ rip = locphysreg(Rrip);
+ l = locmeml(htget(s->globls, n), rip, NULL, mode(n));
+ } else if (hthas(s->stkoff, n)) {
+ stkoff = ptoi(htget(s->stkoff, n));
+ l = locmem(-stkoff, locphysreg(Rrbp), NULL, mode(n));
+ } else {
+ if (!hthas(s->reglocs, n))
+ htput(s->reglocs, n, locreg(mode(n)));
+ return htget(s->reglocs, n);
+ }
+ break;
+ case Olit:
+ v = n->expr.args[0];
+ switch (v->lit.littype) {
+ case Lchr: l = loclit(v->lit.chrval, mode(n)); break;
+ case Lbool: l = loclit(v->lit.boolval, mode(n)); break;
+ case Lint: l = loclit(v->lit.intval, mode(n)); break;
+ default:
+ die("Literal type %s should be blob", litstr(v->lit.littype));
+ }
+ break;
+ default:
+ die("Node %s not leaf in loc()", opstr(exprop(n)));
+ break;
+ }
+ return l;
+}
+
+static Insn *mkinsnv(AsmOp op, va_list ap)
+{
+ Loc *l;
+ Insn *i;
+ int n;
+
+ n = 0;
+ i = malloc(sizeof(Insn));
+ i->op = op;
+ while ((l = va_arg(ap, Loc*)) != NULL)
+ i->args[n++] = l;
+ i->nargs = n;
+ return i;
+}
+
+Insn *mkinsn(AsmOp op, ...)
+{
+ va_list ap;
+ Insn *i;
+
+ va_start(ap, op);
+ i = mkinsnv(op, ap);
+ va_end(ap);
+ return i;
+}
+
+void g(Isel *s, AsmOp op, ...)
+{
+ va_list ap;
+ Insn *i;
+
+ va_start(ap, op);
+ i = mkinsnv(op, ap);
+ va_end(ap);
+ if (debugopt['i']) {
+ printf("GEN ");
+ iprintf(stdout, i);
+ }
+ lappend(&s->curbb->il, &s->curbb->ni, i);
+}
+
+static void movz(Isel *s, Loc *src, Loc *dst)
+{
+ if (src->mode == dst->mode)
+ g(s, Imov, src, dst, NULL);
+ else
+ g(s, Imovzx, src, dst, NULL);
+}
+
+static void load(Isel *s, Loc *a, Loc *b)
+{
+ Loc *l;
+
+ assert(b->type == Locreg);
+ if (a->type == Locreg)
+ l = locmem(0, b, Rnone, a->mode);
+ else
+ l = a;
+ if (isfloatmode(b->mode))
+ g(s, Imovs, l, b, NULL);
+ else
+ g(s, Imov, l, b, NULL);
+}
+
+static void stor(Isel *s, Loc *a, Loc *b)
+{
+ Loc *l;
+
+ assert(a->type == Locreg || a->type == Loclit);
+ if (b->type == Locreg)
+ l = locmem(0, b, Rnone, b->mode);
+ else
+ l = b;
+ if (isfloatmode(b->mode))
+ g(s, Imovs, a, l, NULL);
+ else
+ g(s, Imov, a, l, NULL);
+}
+
+/* ensures that a location is within a reg */
+static Loc *newr(Isel *s, Loc *a)
+{
+ Loc *r;
+
+ r = locreg(a->mode);
+ if (a->type == Locreg) {
+ if (isfloatmode(a->mode))
+ g(s, Imovs, a, r, NULL);
+ else
+ g(s, Imov, a, r, NULL);
+ } else {
+ load(s, a, r);
+ }
+ return r;
+}
+
+static Loc *inr(Isel *s, Loc *a)
+{
+ if (a->type == Locreg)
+ return a;
+ return newr(s, a);
+}
+
+/* ensures that a location is within a reg or an imm */
+static Loc *inri(Isel *s, Loc *a)
+{
+ if (a->type == Locreg || a->type == Loclit)
+ return a;
+ else
+ return newr(s, a);
+}
+
+/* If we're testing equality, etc, it's a bit silly
+ * to generate the test, store it to a bite, expand it
+ * to the right width, and then test it again. Try to optimize
+ * for these common cases.
+ *
+ * if we're doing the optimization to avoid
+ * multiple tests, we want to eval the children
+ * of the first arg, instead of the first arg
+ * directly */
+static void selcjmp(Isel *s, Node *n, Node **args)
+{
+ Loc *a, *b;
+ Loc *l1, *l2;
+ AsmOp cond, jmp;
+
+ cond = reloptab[exprop(args[0])].test;
+ jmp = reloptab[exprop(args[0])].jmp;
+ /* if we have a cond, we're knocking off the redundant test,
+ * and want to eval the children */
+ if (cond) {
+ a = selexpr(s, args[0]->expr.args[0]);
+ if (args[0]->expr.nargs == 2)
+ b = selexpr(s, args[0]->expr.args[1]);
+ else
+ b = a;
+ a = newr(s, a);
+ } else {
+ cond = Itest;
+ jmp = Ijnz;
+ b = newr(s, selexpr(s, args[0])); /* cond */
+ a = b;
+ }
+
+ /* the jump targets will always be evaluated the same way */
+ l1 = loclbl(args[1]); /* if true */
+ l2 = loclbl(args[2]); /* if false */
+
+ g(s, cond, b, a, NULL);
+ g(s, jmp, l1, NULL);
+ g(s, Ijmp, l2, NULL);
+}
+
+static Loc *binop(Isel *s, AsmOp op, Node *x, Node *y)
+{
+ Loc *a, *b;
+
+ a = selexpr(s, x);
+ b = selexpr(s, y);
+ a = newr(s, a);
+ g(s, op, b, a, NULL);
+ return a;
+}
+
+/* We have a few common cases to optimize here:
+ * Oaddr(expr)
+ * or:
+ * Oadd(
+ * reg,
+ * reg||const)
+ *
+ * or:
+ * Oadd(
+ * reg,
+ * Omul(reg,
+ * 2 || 4 || 8)))
+ */
+static int ismergablemul(Node *n, int *r)
+{
+ int v;
+
+ if (exprop(n) != Omul)
+ return 0;
+ if (exprop(n->expr.args[1]) != Olit)
+ return 0;
+ if (n->expr.args[1]->expr.args[0]->type != Nlit)
+ return 0;
+ if (n->expr.args[1]->expr.args[0]->lit.littype != Lint)
+ return 0;
+ v = n->expr.args[1]->expr.args[0]->lit.intval;
+ if (v != 2 && v != 4 && v != 8)
+ return 0;
+ *r = v;
+ return 1;
+}
+
+static Loc *memloc(Isel *s, Node *e, Mode m)
+{
+ Node **args;
+ Loc *l, *b, *o; /* location, base, offset */
+ int scale;
+
+ scale = 1;
+ l = NULL;
+ args = e->expr.args;
+ if (exprop(e) == Oadd) {
+ b = selexpr(s, args[0]);
+ if (ismergablemul(args[1], &scale))
+ o = selexpr(s, args[1]->expr.args[0]);
+ else
+ o = selexpr(s, args[1]);
+
+ if (b->type != Locreg)
+ b = inr(s, b);
+ if (o->type == Loclit) {
+ l = locmem(scale*o->lit, b, Rnone, m);
+ } else {
+ b = inr(s, b);
+ o = inr(s, o);
+ l = locmems(0, b, o, scale, m);
+ }
+ } else {
+ l = selexpr(s, e);
+ l = inr(s, l);
+ l = locmem(0, l, Rnone, m);
+ }
+ assert(l != NULL);
+ return l;
+}
+
+static void blit(Isel *s, Loc *to, Loc *from, size_t dstoff, size_t srcoff, size_t sz)
+{
+ Loc *sp, *dp, *len; /* pointers to src, dst */
+
+ len = loclit(sz, ModeQ);
+ sp = newr(s, from);
+ dp = newr(s, to);
+
+ /* length to blit */
+ g(s, Imov, len, locphysreg(Rrcx), NULL);
+ /* source address with offset */
+ if (srcoff)
+ g(s, Ilea, locmem(srcoff, sp, NULL, ModeQ), locphysreg(Rrsi), NULL);
+ else
+ g(s, Imov, sp, locphysreg(Rrsi), NULL);
+ /* dest address with offset */
+ if (dstoff)
+ g(s, Ilea, locmem(dstoff, dp, NULL, ModeQ), locphysreg(Rrdi), NULL);
+ else
+ g(s, Imov, dp, locphysreg(Rrdi), NULL);
+ g(s, Irepmovsb, NULL);
+}
+
+static int isfunc(Isel *s, Node *n)
+{
+ Node *d;
+
+ if (exprop(n) != Ovar)
+ return 0;
+ if (!hthas(s->globls, n))
+ return 0;
+ d = decls[n->expr.did];
+ if (d && d->decl.isconst)
+ return tybase(decltype(d))->type == Tyfunc;
+ return 0;
+}
+
+static void call(Isel *s, Node *n)
+{
+ AsmOp op;
+ Loc *f;
+
+ if (isfunc(s, n)) {
+ op = Icall;
+ f = locmeml(htget(s->globls, n), NULL, NULL, mode(n));
+ } else {
+ op = Icallind;
+ f = selexpr(s, n);
+ }
+ g(s, op, f, NULL);
+}
+
+static Loc *gencall(Isel *s, Node *n)
+{
+ Loc *src, *dst, *arg; /* values we reduced */
+ Loc *retloc, *rsp, *ret; /* hard-coded registers */
+ Loc *stkbump; /* calculated stack offset */
+ int argsz, argoff;
+ size_t i;
+
+ rsp = locphysreg(Rrsp);
+ if (tybase(exprtype(n))->type == Tyvoid) {
+ retloc = NULL;
+ ret = NULL;
+ } else if (stacktype(exprtype(n))) {
+ retloc = locphysreg(Rrax);
+ ret = locreg(ModeQ);
+ } else if (floattype(exprtype(n))) {
+ retloc = coreg(Rxmm0d, mode(n));
+ ret = locreg(mode(n));
+ } else {
+ retloc = coreg(Rrax, mode(n));
+ ret = locreg(mode(n));
+ }
+ argsz = 0;
+ /* Have to calculate the amount to bump the stack
+ * pointer by in one pass first, otherwise if we push
+ * one at a time, we evaluate the args in reverse order.
+ * Not good.
+ *
+ * Skip the first operand, since it's the function itself */
+ for (i = 1; i < n->expr.nargs; i++) {
+ argsz = align(argsz, min(size(n->expr.args[i]), Ptrsz));
+ argsz += size(n->expr.args[i]);
+ }
+ argsz = align(argsz, 16);
+ stkbump = loclit(argsz, ModeQ);
+ if (argsz)
+ g(s, Isub, stkbump, rsp, NULL);
+
+ /* Now, we can evaluate the arguments */
+ argoff = 0;
+ for (i = 1; i < n->expr.nargs; i++) {
+ arg = selexpr(s, n->expr.args[i]);
+ argoff = align(argoff, min(size(n->expr.args[i]), Ptrsz));
+ if (stacknode(n->expr.args[i])) {
+ src = locreg(ModeQ);
+ g(s, Ilea, arg, src, NULL);
+ blit(s, rsp, src, argoff, 0, size(n->expr.args[i]));
+ } else {
+ dst = locmem(argoff, rsp, NULL, arg->mode);
+ arg = inri(s, arg);
+ stor(s, arg, dst);
+ }
+ argoff += size(n->expr.args[i]);
+ }
+ call(s, n->expr.args[0]);
+ if (argsz)
+ g(s, Iadd, stkbump, rsp, NULL);
+ if (retloc) {
+ if (isfloatmode(retloc->mode))
+ g(s, Imovs, retloc, ret, NULL);
+ else
+ g(s, Imov, retloc, ret, NULL);
+ }
+ return ret;
+}
+
+Loc *selexpr(Isel *s, Node *n)
+{
+ Loc *a, *b, *c, *d, *r;
+ Loc *eax, *edx, *cl; /* x86 wants some hard-coded regs */
+ Node **args;
+
+ args = n->expr.args;
+ eax = locphysreg(Reax);
+ edx = locphysreg(Redx);
+ cl = locphysreg(Rcl);
+ r = NULL;
+ switch (exprop(n)) {
+ case Oadd: r = binop(s, Iadd, args[0], args[1]); break;
+ case Osub: r = binop(s, Isub, args[0], args[1]); break;
+ case Obor: r = binop(s, Ior, args[0], args[1]); break;
+ case Oband: r = binop(s, Iand, args[0], args[1]); break;
+ case Obxor: r = binop(s, Ixor, args[0], args[1]); break;
+ case Omul:
+ if (size(args[0]) == 1) {
+ a = selexpr(s, args[0]);
+ b = selexpr(s, args[1]);
+
+ c = locphysreg(Ral);
+ r = locreg(a->mode);
+ g(s, Imov, a, c, NULL);
+ g(s, Iimul_r, b, NULL);
+ g(s, Imov, c, r, NULL);
+ } else {
+ r = binop(s, Iimul, args[0], args[1]);
+ }
+ break;
+ case Odiv:
+ case Omod:
+ /* these get clobbered by the div insn */
+ a = selexpr(s, args[0]);
+ b = selexpr(s, args[1]);
+ b = newr(s, b);
+ c = coreg(Reax, mode(n));
+ r = locreg(a->mode);
+ if (r->mode == ModeB)
+ g(s, Ixor, eax, eax, NULL);
+ else
+ g(s, Ixor, edx, edx, NULL);
+ g(s, Imov, a, c, NULL);
+ g(s, Idiv, b, NULL);
+ if (exprop(n) == Odiv)
+ d = coreg(Reax, mode(n));
+ else if (r->mode != ModeB)
+ d = coreg(Redx, mode(n));
+ else
+ d = locphysreg(Rah);
+ g(s, Imov, d, r, NULL);
+ break;
+ case Oneg:
+ r = selexpr(s, args[0]);
+ r = newr(s, r);
+ g(s, Ineg, r, NULL);
+ break;
+
+ /* fp expressions */
+ case Ofadd: r = binop(s, Iadds, args[0], args[1]); break;
+ case Ofsub: r = binop(s, Isubs, args[0], args[1]); break;
+ case Ofmul: r = binop(s, Imuls, args[0], args[1]); break;
+ case Ofdiv: r = binop(s, Idivs, args[0], args[1]); break;
+ case Ofneg:
+ r = selexpr(s, args[0]);
+ r = newr(s, r);
+ a = NULL;
+ b = NULL;
+ if (mode(args[0]) == ModeF) {
+ a = locreg(ModeF);
+ b = loclit(1LL << (31), ModeF);
+ g(s, Imovs, r, a);
+ } else if (mode(args[0]) == ModeD) {
+ a = locreg(ModeQ);
+ b = loclit(1LL << 63, ModeQ);
+ g(s, Imov, r, a, NULL);
+ }
+ g(s, Ixor, b, a, NULL);
+ g(s, Imov, a, r, NULL);
+ break;
+ case Obsl:
+ case Obsr:
+ a = newr(s, selexpr(s, args[0]));
+ b = selexpr(s, args[1]);
+ if (b->type == Loclit) {
+ d = b;
+ } else {
+ c = coreg(Rcl, b->mode);
+ g(s, Imov, b, c, NULL);
+ d = cl;
+ }
+ if (exprop(n) == Obsr) {
+ if (istysigned(n->expr.type))
+ g(s, Isar, d, a, NULL);
+ else
+ g(s, Ishr, d, a, NULL);
+ } else {
+ g(s, Ishl, d, a, NULL);
+ }
+ r = a;
+ break;
+ case Obnot:
+ r = selexpr(s, args[0]);
+ r = newr(s, r);
+ g(s, Inot, r, NULL);
+ break;
+
+ case Oderef:
+ r = memloc(s, args[0], mode(n));
+ break;
+
+ case Oaddr:
+ a = selexpr(s, args[0]);
+ if (a->type == Loclbl || (a->type == Locmeml && !a->mem.base)) {
+ r = loclitl(a->lbl);
+ } else {
+ r = locreg(ModeQ);
+ g(s, Ilea, a, r, NULL);
+ }
+ break;
+
+ case Olnot:
+ a = newr(s, selexpr(s, args[0]));
+ b = locreg(ModeB);
+ r = locreg(mode(n));
+ /* lnot only valid for integer-like values */
+ g(s, reloptab[exprop(n)].test, a, a, NULL);
+ g(s, reloptab[exprop(n)].getflag, b, NULL);
+ movz(s, b, r);
+ break;
+
+ case Oeq: case One: case Ogt: case Oge: case Olt: case Ole:
+ case Ofeq: case Ofne: case Ofgt: case Ofge: case Oflt: case Ofle:
+ case Oueq: case Oune: case Ougt: case Ouge: case Oult: case Oule:
+ a = selexpr(s, args[0]);
+ b = selexpr(s, args[1]);
+ a = newr(s, a);
+ c = locreg(ModeB);
+ r = locreg(mode(n));
+ g(s, reloptab[exprop(n)].test, b, a, NULL);
+ g(s, reloptab[exprop(n)].getflag, c, NULL);
+ movz(s, c, r);
+ return r;
+
+ case Oasn: /* relabel */
+ die("Unimplemented op %s", opstr(exprop(n)));
+ break;
+ case Oset:
+ assert(exprop(args[0]) == Ovar || exprop(args[0]) == Oderef);
+ b = selexpr(s, args[1]);
+ if (exprop(args[0]) == Oderef)
+ a = memloc(s, args[0]->expr.args[0], mode(n));
+ else
+ a = selexpr(s, args[0]);
+ b = inri(s, b);
+ if (isfloatmode(b->mode))
+ g(s, Imovs, b, a, NULL);
+ else
+ g(s, Imov, b, a, NULL);
+ r = b;
+ break;
+ case Ocall:
+ r = gencall(s, n);
+ break;
+ case Ojmp:
+ g(s, Ijmp, a = loclbl(args[0]), NULL);
+ break;
+ case Ocjmp:
+ selcjmp(s, n, args);
+ break;
+
+ case Olit: /* fall through */
+ r = loc(s, n);
+ break;
+ case Ovar:
+ if (isfunc(s, n)) {
+ r = locreg(ModeQ);
+ a = loc(s, n);
+ g(s, Ilea, a, r, NULL);
+ } else {
+ r = loc(s, n);
+ }
+ break;
+ case Olbl:
+ r = loclbl(args[0]);
+ break;
+ case Oblit:
+ a = selexpr(s, args[0]);
+ r = selexpr(s, args[1]);
+ blit(s, a, r, 0, 0, args[2]->expr.args[0]->lit.intval);
+ break;
+ case Otrunc:
+ a = selexpr(s, args[0]);
+ a = inr(s, a);
+ r = locreg(mode(n));
+ g(s, Imov, a, r, NULL);
+ break;
+ case Ozwiden:
+ a = selexpr(s, args[0]);
+ a = inr(s, a);
+ r = locreg(mode(n));
+ movz(s, a, r);
+ break;
+ case Oswiden:
+ a = selexpr(s, args[0]);
+ a = inr(s, a);
+ r = locreg(mode(n));
+ g(s, Imovsx, a, r, NULL);
+ break;
+ case Oint2flt:
+ a = selexpr(s, args[0]);
+ b = locreg(ModeQ);
+ r = locreg(mode(n));
+ g(s, Imovs, a, b, NULL);
+ g(s, Icvttsi2sd, b, r, NULL);
+ break;
+ case Oflt2int:
+ a = selexpr(s, args[0]);
+ b = locreg(ModeQ);
+ r = locreg(mode(n));
+ g(s, Icvttsd2si, a, b, NULL);
+ g(s, Imov, b, r, NULL);
+ break;
+
+ /* These operators should never show up in the reduced trees,
+ * since they should have been replaced with more primitive
+ * expressions by now */
+ case Obad: case Oret: case Opreinc: case Opostinc: case Opredec:
+ case Opostdec: case Olor: case Oland: case Oaddeq:
+ case Osubeq: case Omuleq: case Odiveq: case Omodeq: case Oboreq:
+ case Obandeq: case Obxoreq: case Obsleq: case Obsreq: case Omemb:
+ case Oslice: case Oidx: case Osize: case Numops:
+ case Oucon: case Ouget: case Otup: case Oarr: case Ostruct:
+ case Oslbase: case Osllen: case Ocast:
+ case Obreak: case Ocontinue:
+ dump(n, stdout);
+ die("Should not see %s in isel", opstr(exprop(n)));
+ break;
+ }
+ return r;
+}
+
+void locprint(FILE *fd, Loc *l, char spec)
+{
+ assert(l->mode);
+ switch (l->type) {
+ case Loclitl:
+ assert(spec == 'i' || spec == 'x' || spec == 'u');
+ fprintf(fd, "$%s", l->lbl);
+ break;
+ case Loclbl:
+ assert(spec == 'm' || spec == 'v' || spec == 'x');
+ fprintf(fd, "%s", l->lbl);
+ break;
+ case Locreg:
+ assert((spec == 'r' && isintmode(l->mode)) ||
+ (spec == 'f' && isfloatmode(l->mode)) ||
+ spec == 'v' ||
+ spec == 'x' ||
+ spec == 'u');
+ if (l->reg.colour == Rnone)
+ fprintf(fd, "%%P.%zd", l->reg.id);
+ else
+ fprintf(fd, "%s", regnames[l->reg.colour]);
+ break;
+ case Locmem:
+ case Locmeml:
+ assert(spec == 'm' || spec == 'v' || spec == 'x');
+ if (l->type == Locmem) {
+ if (l->mem.constdisp)
+ fprintf(fd, "%ld", l->mem.constdisp);
+ } else {
+ if (l->mem.lbldisp)
+ fprintf(fd, "%s", l->mem.lbldisp);
+ }
+ if (l->mem.base) {
+ fprintf(fd, "(");
+ locprint(fd, l->mem.base, 'r');
+ if (l->mem.idx) {
+ fprintf(fd, ",");
+ locprint(fd, l->mem.idx, 'r');
+ }
+ if (l->mem.scale > 1)
+ fprintf(fd, ",%d", l->mem.scale);
+ if (l->mem.base)
+ fprintf(fd, ")");
+ } else if (l->type != Locmeml) {
+ die("Only locmeml can have unspecified base reg");
+ }
+ break;
+ case Loclit:
+ assert(spec == 'i' || spec == 'x' || spec == 'u');
+ fprintf(fd, "$%ld", l->lit);
+ break;
+ case Locnone:
+ die("Bad location in locprint()");
+ break;
+ }
+}
+
+int subreg(Loc *a, Loc *b)
+{
+ return rclass(a) == rclass(b) && a->mode != b->mode;
+}
+
+void iprintf(FILE *fd, Insn *insn)
+{
+ char *p;
+ int i;
+ int modeidx;
+
+ /* x64 has a quirk; it has no movzlq because mov zero extends. This
+ * means that we need to do a movl when we really want a movzlq. Since
+ * we don't know the name of the reg to use, we need to sub it in when
+ * writing... */
+ switch (insn->op) {
+ case Imovzx:
+ if (insn->args[0]->mode == ModeL && insn->args[1]->mode == ModeQ) {
+ if (insn->args[1]->reg.colour) {
+ insn->op = Imov;
+ insn->args[1] = coreg(insn->args[1]->reg.colour, ModeL);
+ }
+ }
+ break;
+ case Imovs:
+ /* moving a reg to itself is dumb. */
+ if (insn->args[0]->reg.colour == insn->args[1]->reg.colour)
+ return;
+ break;
+ case Imov:
+ assert(!isfloatmode(insn->args[1]->mode));
+ if (insn->args[0]->type != Locreg || insn->args[1]->type != Locreg)
+ break;
+ if (insn->args[0]->reg.colour == Rnone || insn->args[1]->reg.colour == Rnone)
+ break;
+ /* if one reg is a subreg of another, we can just use the right
+ * mode to move between them. */
+ if (subreg(insn->args[0], insn->args[1]))
+ insn->args[0] = coreg(insn->args[0]->reg.colour, insn->args[1]->mode);
+ /* moving a reg to itself is dumb. */
+ if (insn->args[0]->reg.colour == insn->args[1]->reg.colour)
+ return;
+ break;
+ default:
+ break;
+ }
+ p = insnfmts[insn->op];
+ i = 0;
+ modeidx = 0;
+ for (; *p; p++) {
+ if (*p != '%') {
+ fputc(*p, fd);
+ continue;
+ }
+
+ /* %-formating */
+ p++;
+ switch (*p) {
+ case '\0':
+ goto done; /* skip the final p++ */
+ case 'r': /* int register */
+ case 'f': /* float register */
+ case 'm': /* memory */
+ case 'i': /* imm */
+ case 'v': /* reg/mem */
+ case 'u': /* reg/imm */
+ case 'x': /* reg/mem/imm */
+ locprint(fd, insn->args[i], *p);
+ i++;
+ break;
+ case 't':
+ default:
+ /* the asm description uses 1-based indexing, so that 0
+ * can be used as a sentinel. */
+ if (isdigit(*p))
+ modeidx = strtol(p, &p, 10) - 1;
+
+ if (*p == 't')
+ fputs(modenames[insn->args[modeidx]->mode], fd);
+ else
+ die("Invalid %%-specifier '%c'", *p);
+ break;
+ }
+ }
+done:
+ return;
+}
+
+static void isel(Isel *s, Node *n)
+{
+ switch (n->type) {
+ case Nexpr:
+ selexpr(s, n);
+ break;
+ case Ndecl:
+ break;
+ default:
+ die("Bad node type in isel()");
+ break;
+ }
+}
+
+Reg savedregs[] = {
+ Rrcx, Rrdx, Rrbx, Rrsi, Rrdi, Rr8, Rr9, Rr10, Rr11, Rr12, Rr13, Rr14, Rr15,
+ /*
+ Rxmm0d, Rxmm1d, Rxmm2d, Rxmm3d, Rxmm4d, Rxmm5d, Rxmm6d, Rxmm7d,
+ Rxmm8d, Rxmm9d, Rxmm10d, Rxmm11d, Rxmm12d, Rxmm13d, Rxmm14d, Rxmm15d,
+ */
+};
+
+static void prologue(Isel *s, size_t sz)
+{
+ Loc *rsp;
+ Loc *rbp;
+ Loc *stksz;
+ size_t i;
+
+ rsp = locphysreg(Rrsp);
+ rbp = locphysreg(Rrbp);
+ stksz = loclit(sz, ModeQ);
+ /* enter function */
+ g(s, Ipush, rbp, NULL);
+ g(s, Imov, rsp, rbp, NULL);
+ g(s, Isub, stksz, rsp, NULL);
+ /* save registers */
+ for (i = 0; i < sizeof(savedregs)/sizeof(savedregs[0]); i++) {
+ s->calleesave[i] = locreg(ModeQ);
+ g(s, Imov, locphysreg(savedregs[i]), s->calleesave[i], NULL);
+ }
+ s->stksz = stksz; /* need to update if we spill */
+}
+
+static void epilogue(Isel *s)
+{
+ Loc *rsp, *rbp;
+ Loc *ret;
+ size_t i;
+
+ rsp = locphysreg(Rrsp);
+ rbp = locphysreg(Rrbp);
+ if (s->ret) {
+ ret = loc(s, s->ret);
+ if (floattype(exprtype(s->ret)))
+ g(s, Imovs, ret, coreg(Rxmm0d, ret->mode), NULL);
+ else
+ g(s, Imov, ret, coreg(Rax, ret->mode), NULL);
+ }
+ /* restore registers */
+ for (i = 0; i < Nsaved; i++)
+ g(s, Imov, s->calleesave[i], locphysreg(savedregs[i]), NULL);
+ /* leave function */
+ g(s, Imov, rbp, rsp, NULL);
+ g(s, Ipop, rbp, NULL);
+ g(s, Iret, NULL);
+}
+
+static void writeasm(FILE *fd, Isel *s, Func *fn)
+{
+ size_t i, j;
+
+ if (fn->isexport || !strcmp(fn->name, Symprefix "main"))
+ fprintf(fd, ".globl %s\n", fn->name);
+ fprintf(fd, "%s:\n", fn->name);
+ for (j = 0; j < s->cfg->nbb; j++) {
+ for (i = 0; i < s->bb[j]->nlbls; i++)
+ fprintf(fd, "%s:\n", s->bb[j]->lbls[i]);
+ for (i = 0; i < s->bb[j]->ni; i++)
+ iprintf(fd, s->bb[j]->il[i]);
+ }
+}
+
+static Asmbb *mkasmbb(Bb *bb)
+{
+ Asmbb *as;
+
+ as = zalloc(sizeof(Asmbb));
+ as->id = bb->id;
+ as->pred = bsdup(bb->pred);
+ as->succ = bsdup(bb->succ);
+ as->lbls = memdup(bb->lbls, bb->nlbls*sizeof(char*));
+ as->nlbls = bb->nlbls;
+ return as;
+}
+
+static void writebytes(FILE *fd, char *p, size_t sz)
+{
+ size_t i;
+
+ for (i = 0; i < sz; i++) {
+ if (i % 60 == 0)
+ fprintf(fd, "\t.ascii \"");
+ if (p[i] == '"' || p[i] == '\\')
+ fprintf(fd, "\\");
+ if (isprint(p[i]))
+ fprintf(fd, "%c", p[i]);
+ else
+ fprintf(fd, "\\%03o", (uint8_t)p[i] & 0xff);
+ /* line wrapping for readability */
+ if (i % 60 == 59 || i == sz - 1)
+ fprintf(fd, "\"\n");
+ }
+}
+
+static size_t writelit(FILE *fd, Htab *strtab, Node *v, Type *ty)
+{
+ char buf[128];
+ char *lbl;
+ size_t sz;
+ char *intsz[] = {
+ [1] = ".byte",
+ [2] = ".short",
+ [4] = ".long",
+ [8] = ".quad"
+ };
+ union {
+ float fv;
+ double dv;
+ uint64_t qv;
+ uint32_t lv;
+ } u;
+
+ assert(v->type == Nlit);
+ sz = tysize(ty);
+ switch (v->lit.littype) {
+ case Lint: fprintf(fd, "\t%s %lld\n", intsz[sz], v->lit.intval); break;
+ case Lbool: fprintf(fd, "\t.byte %d\n", v->lit.boolval); break;
+ case Lchr: fprintf(fd, "\t.long %d\n", v->lit.chrval); break;
+ case Lflt:
+ if (tybase(v->lit.type)->type == Tyfloat32) {
+ u.fv = v->lit.fltval;
+ fprintf(fd, "\t.long 0x%"PRIx32"\n", u.lv);
+ } else if (tybase(v->lit.type)->type == Tyfloat64) {
+ u.dv = v->lit.fltval;
+ fprintf(fd, "\t.quad 0x%"PRIx64"\n", u.qv);
+ }
+ break;
+ case Lstr:
+ if (hthas(strtab, v->lit.strval)) {
+ lbl = htget(strtab, v->lit.strval);
+ } else {
+ lbl = genlblstr(buf, sizeof buf);
+ htput(strtab, v->lit.strval, strdup(lbl));
+ }
+ fprintf(fd, "\t.quad %s\n", lbl);
+ fprintf(fd, "\t.quad %zd\n", strlen(v->lit.strval));
+ break;
+ case Lfunc:
+ die("Generating this shit ain't ready yet ");
+ break;
+ case Llbl:
+ die("Can't generate literal labels, ffs. They're not data.");
+ break;
+ }
+ return sz;
+}
+
+static size_t writepad(FILE *fd, size_t sz)
+{
+ assert((ssize_t)sz >= 0);
+ if (sz > 0)
+ fprintf(fd, "\t.fill %zd,1,0\n", sz);
+ return sz;
+}
+
+static size_t getintlit(Node *n, char *failmsg)
+{
+ if (exprop(n) != Olit)
+ fatal(n->line, "%s", failmsg);
+ n = n->expr.args[0];
+ if (n->lit.littype != Lint)
+ fatal(n->line, "%s", failmsg);
+ return n->lit.intval;
+}
+
+static size_t writeslice(FILE *fd, Htab *globls, Htab *strtab, Node *n)
+{
+ Node *base, *lo, *hi;
+ ssize_t loval, hival, sz;
+ char *lbl;
+
+ base = n->expr.args[0];
+ lo = n->expr.args[1];
+ hi = n->expr.args[2];
+
+ /* by this point, all slicing operations should have had their bases
+ * pulled out, and we should have vars with their pseudo-decls in their
+ * place */
+ if (exprop(base) != Ovar || !base->expr.isconst)
+ fatal(base->line, "slice base is not a constant value");
+ loval = getintlit(lo, "lower bound in slice is not constant literal");
+ hival = getintlit(hi, "upper bound in slice is not constant literal");
+ sz = tysize(tybase(exprtype(base))->sub[0]);
+
+ lbl = htget(globls, base);
+ fprintf(fd, "\t.quad %s + (%zd*%zd)\n", lbl, loval, sz);
+ fprintf(fd, "\t.quad %zd\n", (hival - loval));
+ return size(n);
+}
+
+static size_t writestruct(FILE *fd, Htab *globls, Htab *strtab, Node *n)
+{
+ Type *t;
+ Node **dcl;
+ int found;
+ size_t i, j;
+ size_t sz, pad, end;
+ size_t ndcl;
+
+ sz = 0;
+ t = tybase(exprtype(n));
+ assert(t->type == Tystruct);
+ dcl = t->sdecls;
+ ndcl = t->nmemb;
+ for (i = 0; i < ndcl; i++) {
+ pad = alignto(sz, decltype(dcl[i]));
+ sz += writepad(fd, pad - sz);
+ found = 0;
+ for (j = 0; j < n->expr.nargs; j++)
+ if (!strcmp(namestr(n->expr.args[j]->expr.idx), declname(dcl[i]))) {
+ found = 1;
+ sz += writeblob(fd, globls, strtab, n->expr.args[j]);
+ }
+ if (!found)
+ sz += writepad(fd, size(dcl[i]));
+ }
+ end = alignto(sz, t);
+ sz += writepad(fd, end - sz);
+ return sz;
+}
+
+static size_t writeblob(FILE *fd, Htab *globls, Htab *strtab, Node *n)
+{
+ size_t i, sz;
+
+ switch(exprop(n)) {
+ case Otup:
+ case Oarr:
+ sz = 0;
+ for (i = 0; i < n->expr.nargs; i++)
+ sz += writeblob(fd, globls, strtab, n->expr.args[i]);
+ break;
+ case Ostruct:
+ sz = writestruct(fd, globls, strtab, n);
+ break;
+ case Olit:
+ sz = writelit(fd, strtab, n->expr.args[0], exprtype(n));
+ break;
+ case Oslice:
+ sz = writeslice(fd, globls, strtab, n);
+ break;
+ default:
+ dump(n, stdout);
+ die("Nonliteral initializer for global");
+ break;
+ }
+ return sz;
+}
+
+void genblob(FILE *fd, Node *blob, Htab *globls, Htab *strtab)
+{
+ char *lbl;
+
+ /* lits and such also get wrapped in decls */
+ assert(blob->type == Ndecl);
+
+ lbl = htget(globls, blob);
+ if (blob->decl.vis != Visintern)
+ fprintf(fd, ".globl %s\n", lbl);
+ fprintf(fd, "%s:\n", lbl);
+ if (blob->decl.init)
+ writeblob(fd, globls, strtab, blob->decl.init);
+ else
+ writepad(fd, size(blob));
+}
+
+/* genasm requires all nodes in 'nl' to map cleanly to operations that are
+ * natively supported, as promised in the output of reduce(). No 64-bit
+ * operations on x32, no structures, and so on. */
+void genasm(FILE *fd, Func *fn, Htab *globls, Htab *strtab)
+{
+ Isel is = {0,};
+ size_t i, j;
+ char buf[128];
+
+ is.reglocs = mkht(varhash, vareq);
+ is.stkoff = fn->stkoff;
+ is.globls = globls;
+ is.ret = fn->ret;
+ is.cfg = fn->cfg;
+ /* ensure that all physical registers have a loc created, so we
+ * don't get any surprises referring to them in the allocator */
+ for (i = 0; i < Nreg; i++)
+ locphysreg(i);
+
+ for (i = 0; i < fn->cfg->nbb; i++)
+ lappend(&is.bb, &is.nbb, mkasmbb(fn->cfg->bb[i]));
+
+ is.curbb = is.bb[0];
+ prologue(&is, fn->stksz);
+ for (j = 0; j < fn->cfg->nbb - 1; j++) {
+ is.curbb = is.bb[j];
+ for (i = 0; i < fn->cfg->bb[j]->nnl; i++) {
+ /* put in a comment that says where this line comes from */
+ snprintf(buf, sizeof buf, "\n\t# bb = %zd, bbidx = %zd, line=%d",
+ j, i, fn->cfg->bb[j]->nl[i]->line);
+ g(&is, Ilbl, locstrlbl(buf), NULL);
+ isel(&is, fn->cfg->bb[j]->nl[i]);
+ }
+ }
+ is.curbb = is.bb[is.nbb - 1];
+ epilogue(&is);
+ regalloc(&is);
+
+ if (debugopt['i'])
+ writeasm(stdout, &is, fn);
+ writeasm(fd, &is, fn);
+}
+
+void genstrings(FILE *fd, Htab *strtab)
+{
+ void **k;
+ size_t i, nk;
+
+ k = htkeys(strtab, &nk);
+ for (i = 0; i < nk; i++) {
+ fprintf(fd, "%s:\n", (char*)htget(strtab, k[i]));
+ writebytes(fd, k[i], strlen(k[i]));
+ }
+}
--- /dev/null
+++ b/6/locs.c
@@ -1,0 +1,303 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+#include "opt.h"
+#include "asm.h"
+
+Mode regmodes[] = {
+#define Reg(r, name, mode) mode,
+#include "regs.def"
+#undef Reg
+};
+
+char *regnames[] = {
+#define Reg(r, name, mode) name,
+#include "regs.def"
+#undef Reg
+};
+
+size_t modesize[Nmode] = {
+ [ModeNone] = 0,
+ [ModeB] = 1,
+ [ModeW] = 2,
+ [ModeL] = 4,
+ [ModeQ] = 8,
+ [ModeF] = 4,
+ [ModeD] = 8,
+};
+
+
+const Reg reginterferes[Nreg][Nmode + 1] = {
+ /* byte */
+ [Ral] = {Ral, Rax, Reax},
+ [Rcl] = {Rcl, Rcx, Recx},
+ [Rdl] = {Rdl, Rdx, Redx},
+ [Rbl] = {Rbl, Rbx, Rebx},
+
+ /* word */
+ [Rax] = {Ral, Rax, Reax},
+ [Rcx] = {Rcl, Rcx, Recx},
+ [Rdx] = {Rdl, Rdx, Redx},
+ [Rbx] = {Rbl, Rbx, Rebx},
+ [Rsi] = {Rsi, Resi},
+ [Rdi] = {Rdi, Redi},
+
+ /* dword */
+ [Reax] = {Ral, Rax, Reax},
+ [Recx] = {Rcl, Rcx, Recx},
+ [Redx] = {Rdl, Rdx, Redx},
+ [Rebx] = {Rbl, Rbx, Rebx},
+ [Resi] = {Rsi, Resi},
+ [Redi] = {Rdi, Redi},
+ [Resp] = {Resp},
+ [Rebp] = {Rebp},
+};
+
+char *genlblstr(char *buf, size_t sz)
+{
+ static int nextlbl;
+ snprintf(buf, 128, ".L%d", nextlbl++);
+ return buf;
+}
+
+Node *genlbl(void)
+{
+ char buf[128];
+
+ genlblstr(buf, 128);
+ return mklbl(-1, buf);
+}
+
+Loc *locstrlbl(char *lbl)
+{
+ Loc *l;
+
+ l = zalloc(sizeof(Loc));
+ l->type = Loclbl;
+ l->mode = ModeQ;
+ l->lbl = strdup(lbl);
+ return l;
+}
+
+Loc *loclitl(char *lbl)
+{
+ Loc *l;
+
+ l = zalloc(sizeof(Loc));
+ l->type = Loclitl;
+ l->mode = ModeQ;
+ l->lbl = strdup(lbl);
+ return l;
+}
+
+Loc *loclbl(Node *e)
+{
+ Node *lbl;
+ assert(e->type == Nexpr);
+ lbl = e->expr.args[0];
+ assert(lbl->type == Nlit);
+ assert(lbl->lit.littype = Llbl);
+ return locstrlbl(lbl->lit.lblval);
+}
+
+Loc **locmap = NULL;
+size_t maxregid = 0;
+
+static Loc *locregid(regid id, Mode m)
+{
+ Loc *l;
+
+ l = zalloc(sizeof(Loc));
+ l->type = Locreg;
+ l->mode = m;
+ l->reg.id = id;
+ locmap = xrealloc(locmap, maxregid * sizeof(Loc*));
+ locmap[l->reg.id] = l;
+ return l;
+}
+
+Loc *locreg(Mode m)
+{
+ return locregid(maxregid++, m);
+}
+
+Loc *locphysreg(Reg r)
+{
+ static Loc *physregs[Nreg] = {0,};
+
+ if (physregs[r])
+ return physregs[r];
+ physregs[r] = locreg(regmodes[r]);
+ physregs[r]->reg.colour = r;
+ return physregs[r];
+}
+
+Loc *locmem(long disp, Loc *base, Loc *idx, Mode mode)
+{
+ Loc *l;
+
+ l = zalloc(sizeof(Loc));
+ l->type = Locmem;
+ l->mode = mode;
+ l->mem.constdisp = disp;
+ l->mem.base = base;
+ l->mem.idx = idx;
+ l->mem.scale = 0;
+ return l;
+}
+
+Loc *locmems(long disp, Loc *base, Loc *idx, int scale, Mode mode)
+{
+ Loc *l;
+
+ l = locmem(disp, base, idx, mode);
+ l->mem.scale = scale;
+ return l;
+}
+
+Loc *locmeml(char *disp, Loc *base, Loc *idx, Mode mode)
+{
+ Loc *l;
+
+ l = zalloc(sizeof(Loc));
+ l->type = Locmeml;
+ l->mode = mode;
+ l->mem.lbldisp = strdup(disp);
+ l->mem.base = base;
+ l->mem.idx = idx;
+ l->mem.scale = 0;
+ return l;
+}
+
+Loc *locmemls(char *disp, Loc *base, Loc *idx, int scale, Mode mode)
+{
+ Loc *l;
+
+ l = locmeml(disp, base, idx, mode);
+ l->mem.scale = scale;
+ return l;
+}
+
+
+Loc *loclit(long val, Mode m)
+{
+ Loc *l;
+
+ l = zalloc(sizeof(Loc));
+ l->type = Loclit;
+ l->mode = m;
+ l->lit = val;
+ return l;
+}
+
+Loc *coreg(Reg r, Mode m)
+{
+ Reg crtab[][Nmode + 1] = {
+ [Ral] = {Rnone, Ral, Rax, Reax, Rrax},
+ [Rcl] = {Rnone, Rcl, Rcx, Recx, Rrcx},
+ [Rdl] = {Rnone, Rdl, Rdx, Redx, Rrdx},
+ [Rbl] = {Rnone, Rbl, Rbx, Rebx, Rrbx},
+ [Rsil] = {Rnone, Rsil, Rsi, Resi, Rrsi},
+ [Rdil] = {Rnone, Rdil, Rdi, Redi, Rrdi},
+ [Rr8b] = {Rnone, Rr8b, Rr8w, Rr8d, Rr8},
+ [Rr9b] = {Rnone, Rr9b, Rr9w, Rr9d, Rr9},
+ [Rr10b] = {Rnone, Rr10b, Rr10w, Rr10d, Rr10},
+ [Rr11b] = {Rnone, Rr11b, Rr11w, Rr11d, Rr11},
+ [Rr12b] = {Rnone, Rr12b, Rr12w, Rr12d, Rr12},
+ [Rr13b] = {Rnone, Rr13b, Rr13w, Rr13d, Rr13},
+ [Rr14b] = {Rnone, Rr14b, Rr14w, Rr14d, Rr14},
+ [Rr15b] = {Rnone, Rr15b, Rr15w, Rr15d, Rr15},
+
+ [Rax] = {Rnone, Ral, Rax, Reax, Rrax},
+ [Rcx] = {Rnone, Rcl, Rcx, Recx, Rrcx},
+ [Rdx] = {Rnone, Rdl, Rdx, Redx, Rrdx},
+ [Rbx] = {Rnone, Rbl, Rbx, Rebx, Rrbx},
+ [Rsi] = {Rnone, Rsil, Rsi, Resi, Rrsi},
+ [Rdi] = {Rnone, Rsil, Rdi, Redi, Rrdi},
+ [Rr8w] = {Rnone, Rr8b, Rr8w, Rr8d, Rr8},
+ [Rr9w] = {Rnone, Rr9b, Rr9w, Rr9d, Rr9},
+ [Rr10w] = {Rnone, Rr10b, Rr10w, Rr10d, Rr10},
+ [Rr11w] = {Rnone, Rr11b, Rr11w, Rr11d, Rr11},
+ [Rr12w] = {Rnone, Rr12b, Rr12w, Rr12d, Rr12},
+ [Rr13w] = {Rnone, Rr13b, Rr13w, Rr13d, Rr13},
+ [Rr14w] = {Rnone, Rr14b, Rr14w, Rr14d, Rr14},
+ [Rr15w] = {Rnone, Rr15b, Rr15w, Rr15d, Rr15},
+
+ [Reax] = {Rnone, Ral, Rax, Reax, Rrax},
+ [Recx] = {Rnone, Rcl, Rcx, Recx, Rrcx},
+ [Redx] = {Rnone, Rdl, Rdx, Redx, Rrdx},
+ [Rebx] = {Rnone, Rbl, Rbx, Rebx, Rrbx},
+ [Resi] = {Rnone, Rsil, Rsi, Resi, Rrsi},
+ [Redi] = {Rnone, Rsil, Rdi, Redi, Rrdi},
+ [Rr8d] = {Rnone, Rr8b, Rr8w, Rr8d, Rr8},
+ [Rr9d] = {Rnone, Rr9b, Rr9w, Rr9d, Rr9},
+ [Rr10d] = {Rnone, Rr10b, Rr10w, Rr10d, Rr10},
+ [Rr11d] = {Rnone, Rr11b, Rr11w, Rr11d, Rr11},
+ [Rr12d] = {Rnone, Rr12b, Rr12w, Rr12d, Rr12},
+ [Rr13d] = {Rnone, Rr13b, Rr13w, Rr13d, Rr13},
+ [Rr14d] = {Rnone, Rr14b, Rr14w, Rr14d, Rr14},
+ [Rr15d] = {Rnone, Rr15b, Rr15w, Rr15d, Rr15},
+
+ [Rrax] = {Rnone, Ral, Rax, Reax, Rrax},
+ [Rrcx] = {Rnone, Rcl, Rcx, Recx, Rrcx},
+ [Rrdx] = {Rnone, Rdl, Rdx, Redx, Rrdx},
+ [Rrbx] = {Rnone, Rbl, Rbx, Rebx, Rrbx},
+ [Rrsi] = {Rnone, Rsil, Rsi, Resi, Rrsi},
+ [Rrdi] = {Rnone, Rsil, Rdi, Redi, Rrdi},
+ [Rr8] = {Rnone, Rr8b, Rr8w, Rr8d, Rr8},
+ [Rr9] = {Rnone, Rr9b, Rr9w, Rr9d, Rr9},
+ [Rr10] = {Rnone, Rr10b, Rr10w, Rr10d, Rr10},
+ [Rr11] = {Rnone, Rr11b, Rr11w, Rr11d, Rr11},
+ [Rr12] = {Rnone, Rr12b, Rr12w, Rr12d, Rr12},
+ [Rr13] = {Rnone, Rr13b, Rr13w, Rr13d, Rr13},
+ [Rr14] = {Rnone, Rr14b, Rr14w, Rr14d, Rr14},
+ [Rr15] = {Rnone, Rr15b, Rr15w, Rr15d, Rr15},
+
+ [Rxmm0f] = {[ModeF] = Rxmm0f, [ModeD] = Rxmm0d},
+ [Rxmm1f] = {[ModeF] = Rxmm1f, [ModeD] = Rxmm1d},
+ [Rxmm2f] = {[ModeF] = Rxmm2f, [ModeD] = Rxmm2d},
+ [Rxmm3f] = {[ModeF] = Rxmm3f, [ModeD] = Rxmm3d},
+ [Rxmm4f] = {[ModeF] = Rxmm4f, [ModeD] = Rxmm4d},
+ [Rxmm5f] = {[ModeF] = Rxmm5f, [ModeD] = Rxmm5d},
+ [Rxmm6f] = {[ModeF] = Rxmm6f, [ModeD] = Rxmm6d},
+ [Rxmm7f] = {[ModeF] = Rxmm7f, [ModeD] = Rxmm7d},
+ [Rxmm8f] = {[ModeF] = Rxmm8f, [ModeD] = Rxmm8d},
+ [Rxmm9f] = {[ModeF] = Rxmm9f, [ModeD] = Rxmm9d},
+ [Rxmm10f] = {[ModeF] = Rxmm0f, [ModeD] = Rxmm0d},
+ [Rxmm11f] = {[ModeF] = Rxmm1f, [ModeD] = Rxmm1d},
+ [Rxmm12f] = {[ModeF] = Rxmm2f, [ModeD] = Rxmm2d},
+ [Rxmm13f] = {[ModeF] = Rxmm3f, [ModeD] = Rxmm3d},
+ [Rxmm14f] = {[ModeF] = Rxmm4f, [ModeD] = Rxmm4d},
+ [Rxmm15f] = {[ModeF] = Rxmm5f, [ModeD] = Rxmm5d},
+
+ [Rxmm0d] = {[ModeF] = Rxmm0f, [ModeD] = Rxmm0d},
+ [Rxmm1d] = {[ModeF] = Rxmm1f, [ModeD] = Rxmm1d},
+ [Rxmm2d] = {[ModeF] = Rxmm2f, [ModeD] = Rxmm2d},
+ [Rxmm3d] = {[ModeF] = Rxmm3f, [ModeD] = Rxmm3d},
+ [Rxmm4d] = {[ModeF] = Rxmm4f, [ModeD] = Rxmm4d},
+ [Rxmm5d] = {[ModeF] = Rxmm5f, [ModeD] = Rxmm5d},
+ [Rxmm6d] = {[ModeF] = Rxmm6f, [ModeD] = Rxmm6d},
+ [Rxmm7d] = {[ModeF] = Rxmm7f, [ModeD] = Rxmm7d},
+ [Rxmm8d] = {[ModeF] = Rxmm8f, [ModeD] = Rxmm8d},
+ [Rxmm9d] = {[ModeF] = Rxmm9f, [ModeD] = Rxmm9d},
+ [Rxmm10d] = {[ModeF] = Rxmm0f, [ModeD] = Rxmm0d},
+ [Rxmm11d] = {[ModeF] = Rxmm1f, [ModeD] = Rxmm1d},
+ [Rxmm12d] = {[ModeF] = Rxmm2f, [ModeD] = Rxmm2d},
+ [Rxmm13d] = {[ModeF] = Rxmm3f, [ModeD] = Rxmm3d},
+ [Rxmm14d] = {[ModeF] = Rxmm4f, [ModeD] = Rxmm4d},
+ [Rxmm15d] = {[ModeF] = Rxmm5f, [ModeD] = Rxmm5d},
+ };
+
+ assert(crtab[r][m] != Rnone);
+ return locphysreg(crtab[r][m]);
+}
--- /dev/null
+++ b/6/main.c
@@ -1,0 +1,150 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 <err.h>
+
+#include "parse.h"
+#include "opt.h"
+#include "asm.h"
+
+#include "../config.h"
+
+/* FIXME: move into one place...? */
+Node *file;
+char debugopt[128];
+int writeasm;
+char *outfile;
+char **incpaths;
+size_t nincpaths;
+
+static void usage(char *prog)
+{
+ printf("%s [-h] [-o outfile] [-d[dbgopts]] inputs\n", prog);
+ printf("\t-h\tPrint this help\n");
+ printf("\t-S\tWrite out `input.s` when compiling\n");
+ printf("\t-I path\tAdd 'path' to use search path\n");
+ printf("\t-d\tPrint debug dumps. Recognized options: f r p i\n");
+ printf("\t\t\tf: log folded trees\n");
+ printf("\t\t\tl: log lowered pre-cfg trees\n");
+ printf("\t\t\tT: log tree immediately\n");
+ printf("\t\t\tr: log register allocation activity\n");
+ printf("\t\t\ti: log instruction selection activity\n");
+ printf("\t\t\tu: log type unifications\n");
+ printf("\t-o\tOutput to outfile\n");
+ printf("\t-S\tGenerate assembly instead of object code\n");
+}
+
+static void assem(char *asmsrc, char *input)
+{
+ char objfile[1024];
+ char cmd[2048];
+
+ swapsuffix(objfile, 1024, input, ".myr", ".o");
+ snprintf(cmd, 1024, Asmcmd, objfile, asmsrc);
+ if (system(cmd) == -1)
+ die("Couldn't run assembler");
+}
+
+static char *gentemp(char *buf, size_t bufsz, char *path, char *suffix)
+{
+ char *tmpdir;
+ char *base;
+
+ tmpdir = getenv("TMPDIR");
+ if (!tmpdir)
+ tmpdir = "/tmp";
+ base = strrchr(path, '/');
+ if (base)
+ base++;
+ else
+ base = path;
+ snprintf(buf, bufsz, "%s/tmp%lx-%s%s", tmpdir, random(), base, suffix);
+ return buf;
+}
+
+static void genuse(char *path)
+{
+ FILE *f;
+ char buf[1024];
+
+ swapsuffix(buf, sizeof buf, path, ".myr", ".use");
+ f = fopen(buf, "w");
+ if (!f)
+ err(1, "Could not open path %s\n", buf);
+ writeuse(f, file);
+ fclose(f);
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ int i;
+ Stab *globls;
+ char buf[1024];
+
+ while ((opt = getopt(argc, argv, "d:hSo:I:")) != -1) {
+ switch (opt) {
+ case 'o':
+ outfile = optarg;
+ break;
+ case 'S':
+ writeasm = 1;
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ break;
+ case 'd':
+ while (optarg && *optarg) {
+ if (*optarg == 'y')
+ yydebug = 1;
+ debugopt[*optarg++ & 0x7f]++;
+ }
+ break;
+ case 'I':
+ lappend(&incpaths, &nincpaths, optarg);
+ break;
+ default:
+ usage(argv[0]);
+ exit(0);
+ break;
+ }
+ }
+
+ lappend(&incpaths, &nincpaths, Instroot "/lib/myr");
+ for (i = optind; i < argc; i++) {
+ globls = mkstab();
+ tyinit(globls);
+ tokinit(argv[i]);
+ file = mkfile(argv[i]);
+ file->file.exports = mkstab();
+ file->file.globls = globls;
+ yyparse();
+
+ /* before we do anything to the parse */
+ if (debugopt['T'])
+ dump(file, stdout);
+ infer(file);
+ /* after all type inference */
+ if (debugopt['t'])
+ dump(file, stdout);
+
+ if (writeasm) {
+ swapsuffix(buf, sizeof buf, argv[i], ".myr", ".s");
+ } else {
+ gentemp(buf, sizeof buf, argv[i], ".s");
+ }
+ gen(file, buf);
+ assem(buf, argv[i]);
+ genuse(argv[i]);
+ }
+
+ return 0;
+}
--- /dev/null
+++ b/6/platform.h
@@ -1,0 +1,9 @@
+#if defined(__APPLE__) && defined(__MACH__)
+/* for OSX */
+# define Asmcmd "as -g -o %s %s"
+# define Symprefix "_"
+#else
+/* Default to linux */
+# define Asmcmd "as -g -o %s %s"
+# define Symprefix ""
+#endif
--- /dev/null
+++ b/6/ra.c
@@ -1,0 +1,1333 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+
+#include "parse.h"
+#include "opt.h"
+#include "asm.h"
+
+#define Sizetbits (CHAR_BIT*sizeof(size_t)) /* used in graph reprs */
+
+typedef struct Usemap Usemap;
+struct Usemap {
+ int l[Maxarg + 1]; /* location of arg used in instruction's arg list */
+ int r[Maxarg + 1]; /* list of registers used implicitly by instruction */
+};
+
+void wlprint(FILE *fd, char *name, Loc **wl, size_t nwl);
+static int moverelated(Isel *s, regid n);
+static void printedge(FILE *fd, char *msg, size_t a, size_t b);
+
+/* tables of uses/defs by instruction */
+Usemap usetab[] = {
+#define Use(...) {__VA_ARGS__}
+#define Insn(i, fmt, use, def) use,
+#include "insns.def"
+#undef Insn
+#undef Use
+};
+
+Usemap deftab[] = {
+#define Def(...) {__VA_ARGS__}
+#define Insn(i, fmt, use, def) def,
+#include "insns.def"
+#undef Insn
+#undef Def
+};
+
+/* A map of which registers interfere */
+#define Northogonal 32
+Reg regmap[Northogonal][Nmode] = {
+ [0] = {Rnone, Ral, Rax, Reax, Rrax},
+ [1] = {Rnone, Rcl, Rcx, Recx, Rrcx},
+ [2] = {Rnone, Rdl, Rdx, Redx, Rrdx},
+ [3] = {Rnone, Rbl, Rbx, Rebx, Rrbx},
+ [4] = {Rnone, Rsil, Rsi, Resi, Rrsi},
+ [5] = {Rnone, Rdil, Rdi, Redi, Rrdi},
+ [6] = {Rnone, Rr8b, Rr8w, Rr8d, Rr8},
+ [7] = {Rnone, Rr9b, Rr9w, Rr9d, Rr9},
+ [8] = {Rnone, Rr10b, Rr10w, Rr10d, Rr10},
+ [9] = {Rnone, Rr11b, Rr11w, Rr11d, Rr11},
+ [10] = {Rnone, Rr12b, Rr12w, Rr12d, Rr12},
+ [11] = {Rnone, Rr13b, Rr13w, Rr13d, Rr13},
+ [12] = {Rnone, Rr14b, Rr14w, Rr14d, Rr14},
+ [13] = {Rnone, Rr15b, Rr15w, Rr15d, Rr15},
+ [14] = {Rnone, Rnone, Rnone, Rnone, Rnone},
+ [15] = {Rnone, Rnone, Rnone, Rnone, Rnone},
+ [16] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm0f, Rxmm0d},
+ [17] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm1f, Rxmm1d},
+ [18] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm2f, Rxmm2d},
+ [19] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm3f, Rxmm3d},
+ [20] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm4f, Rxmm4d},
+ [21] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm5f, Rxmm5d},
+ [22] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm6f, Rxmm6d},
+ [23] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm7f, Rxmm7d},
+ [24] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm8f, Rxmm8d},
+ [25] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm9f, Rxmm9d},
+ [26] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm10f, Rxmm10d},
+ [27] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm11f, Rxmm11d},
+ [28] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm12f, Rxmm12d},
+ [29] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm13f, Rxmm13d},
+ [30] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm14f, Rxmm14d},
+ [31] = {Rnone, Rnone, Rnone, Rnone, Rnone, Rxmm15f, Rxmm15d},
+};
+
+/* Which regmap entry a register maps to */
+int colourmap[Nreg] = {
+ /* byte */
+ [Ral] = 0, [Rax] = 0, [Reax] = 0, [Rrax] = 0,
+ [Rcl] = 1, [Rcx] = 1, [Recx] = 1, [Rrcx] = 1,
+ [Rdl] = 2, [Rdx] = 2, [Redx] = 2, [Rrdx] = 2,
+ [Rbl] = 3, [Rbx] = 3, [Rebx] = 3, [Rrbx] = 3,
+ [Rsil] = 4, [Rsi] = 4, [Resi] = 4, [Rrsi] = 4,
+ [Rdil] = 5, [Rdi] = 5, [Redi] = 5, [Rrdi] = 5,
+ [Rr8b] = 6, [Rr8w] = 6, [Rr8d] = 6, [Rr8] = 6,
+ [Rr9b] = 7, [Rr9w] = 7, [Rr9d] = 7, [Rr9] = 7,
+ [Rr10b] = 8, [Rr10w] = 8, [Rr10d] = 8, [Rr10] = 8,
+ [Rr11b] = 9, [Rr11w] = 9, [Rr11d] = 9, [Rr11] = 9,
+ [Rr12b] = 10, [Rr12w] = 10, [Rr12d] = 10, [Rr12] = 10,
+ [Rr13b] = 11, [Rr13w] = 11, [Rr13d] = 11, [Rr13] = 11,
+ [Rr14b] = 12, [Rr14w] = 12, [Rr14d] = 12, [Rr14] = 12,
+ [Rr15b] = 13, [Rr15w] = 13, [Rr15d] = 13, [Rr15] = 13,
+
+ /* float */
+ [Rxmm0f] = 16, [Rxmm0d] = 16,
+ [Rxmm1f] = 17, [Rxmm1d] = 17,
+ [Rxmm2f] = 18, [Rxmm2d] = 18,
+ [Rxmm3f] = 19, [Rxmm3d] = 19,
+ [Rxmm4f] = 20, [Rxmm4d] = 20,
+ [Rxmm5f] = 21, [Rxmm5d] = 21,
+ [Rxmm6f] = 22, [Rxmm6d] = 22,
+ [Rxmm7f] = 23, [Rxmm7d] = 23,
+ [Rxmm8f] = 24, [Rxmm8d] = 24,
+ [Rxmm9f] = 25, [Rxmm9d] = 25,
+ [Rxmm10f] = 26, [Rxmm10d] = 26,
+ [Rxmm11f] = 27, [Rxmm11d] = 27,
+ [Rxmm12f] = 28, [Rxmm12d] = 28,
+ [Rxmm13f] = 29, [Rxmm13d] = 29,
+ [Rxmm14f] = 30, [Rxmm14d] = 30,
+ [Rxmm15f] = 31, [Rxmm15d] = 31,
+};
+
+static int _K[Nclass] = {
+ [Classbad] = 0,
+ [Classint] = 14,
+ [Classflt] = 16,
+};
+
+Rclass rclass(Loc *l)
+{
+ switch (l->mode) {
+ case ModeNone: return Classbad;
+ case Nmode: return Classbad;
+ case ModeB: return Classint;
+ case ModeW: return Classint;
+ case ModeL: return Classint;
+ case ModeQ: return Classint;
+
+ case ModeF: return Classflt;
+ case ModeD: return Classflt;
+ }
+ return Classbad;
+}
+
+/* %esp, %ebp are not in the allocatable pool */
+static int isfixreg(Loc *l)
+{
+ if (l->reg.colour == Resp)
+ return 1;
+ if (l->reg.colour == Rebp)
+ return 1;
+ return 0;
+}
+
+static size_t uses(Insn *insn, regid *u)
+{
+ size_t i, j;
+ int k;
+ Loc *m;
+
+ j = 0;
+ /* Add all the registers used and defined. Duplicates
+ * in this list are fine, since they're being added to
+ * a set anyways */
+ for (i = 0; i < Maxarg; i++) {
+ if (!usetab[insn->op].l[i])
+ break;
+ k = usetab[insn->op].l[i] - 1;
+ /* non-registers are handled later */
+ if (insn->args[k]->type == Locreg)
+ if (!isfixreg(insn->args[k]))
+ u[j++] = insn->args[k]->reg.id;
+ }
+ /* some insns don't reflect their defs in the args.
+ * These are explictly listed in the insn description */
+ for (i = 0; i < Maxarg; i++) {
+ if (!usetab[insn->op].r[i])
+ break;
+ /* not a leak; physical registers get memoized */
+ u[j++] = locphysreg(usetab[insn->op].r[i])->reg.id;
+ }
+ /* If the registers are in an address calculation,
+ * they're used no matter what. */
+ for (i = 0; i < insn->nargs; i++) {
+ m = insn->args[i];
+ if (m->type != Locmem && m->type != Locmeml)
+ continue;
+ if (m->mem.base)
+ if (!isfixreg(m->mem.base))
+ u[j++] = m->mem.base->reg.id;
+ if (m->mem.idx)
+ if (!isfixreg(m->mem.base))
+ u[j++] = m->mem.idx->reg.id;
+ }
+ return j;
+}
+
+static size_t defs(Insn *insn, regid *d)
+{
+ size_t i, j;
+ int k;
+
+ j = 0;
+ /* Add all the registers dsed and defined. Duplicates
+ * in this list are fine, since they're being added to
+ * a set anyways */
+ for (i = 0; i < Maxarg; i++) {
+ if (!deftab[insn->op].l[i])
+ break;
+ k = deftab[insn->op].l[i] - 1;
+ if (insn->args[k]->type == Locreg)
+ if (!isfixreg(insn->args[k]))
+ d[j++] = insn->args[k]->reg.id;
+ }
+ /* some insns don't reflect their defs in the args.
+ * These are explictly listed in the insn description */
+ for (i = 0; i < Maxarg; i++) {
+ if (!deftab[insn->op].r[i])
+ break;
+ /* not a leak; physical registers get memoized */
+ d[j++] = locphysreg(deftab[insn->op].r[i])->reg.id;
+ }
+ return j;
+}
+
+/* The uses and defs for an entire BB. */
+static void udcalc(Asmbb *bb)
+{
+ /* up to 2 registers per memloc, so
+ * 2*Maxarg is the maximum number of
+ * uses or defs we can see */
+ regid u[Maxuse], d[Maxdef];
+ size_t nu, nd;
+ size_t i, j;
+
+ bb->use = bsclear(bb->use);
+ bb->def = bsclear(bb->def);
+ for (i = 0; i < bb->ni; i++) {
+ nu = uses(bb->il[i], u);
+ nd = defs(bb->il[i], d);
+ for (j = 0; j < nu; j++)
+ if (!bshas(bb->def, u[j]))
+ bsput(bb->use, u[j]);
+ for (j = 0; j < nd; j++)
+ bsput(bb->def, d[j]);
+ }
+}
+
+static int istrivial(Isel *s, regid r)
+{
+ return s->degree[r] < _K[rclass(locmap[r])];
+}
+
+static void liveness(Isel *s)
+{
+ Bitset *old;
+ Asmbb **bb;
+ ssize_t nbb;
+ ssize_t i;
+ size_t j;
+ int changed;
+
+ bb = s->bb;
+ nbb = s->nbb;
+ for (i = 0; i < nbb; i++) {
+ udcalc(s->bb[i]);
+ bb[i]->livein = bsclear(bb[i]->livein);
+ bb[i]->liveout = bsclear(bb[i]->liveout);
+ }
+
+ changed = 1;
+ while (changed) {
+ changed = 0;
+ for (i = nbb - 1; i >= 0; i--) {
+ old = bsdup(bb[i]->liveout);
+ /* liveout[b] = U(s in succ) livein[s] */
+ for (j = 0; bsiter(bb[i]->succ, &j); j++)
+ bsunion(bb[i]->liveout, bb[j]->livein);
+ /* livein[b] = use[b] U (out[b] \ def[b]) */
+ bb[i]->livein = bsclear(bb[i]->livein);
+ bsunion(bb[i]->livein, bb[i]->liveout);
+ bsdiff(bb[i]->livein, bb[i]->def);
+ bsunion(bb[i]->livein, bb[i]->use);
+ if (!changed)
+ changed = !bseq(old, bb[i]->liveout);
+ }
+ }
+}
+
+/* we're only interested in register->register moves */
+static int ismove(Insn *i)
+{
+ if (i->op != Imov)
+ return 0;
+ return i->args[0]->type == Locreg && i->args[1]->type == Locreg;
+}
+
+static int gbhasedge(Isel *s, size_t u, size_t v)
+{
+ size_t i;
+ i = (maxregid * v) + u;
+ return (s->gbits[i/Sizetbits] & (1ULL <<(i % Sizetbits))) != 0;
+}
+
+static void gbputedge(Isel *s, size_t u, size_t v)
+{
+ size_t i, j;
+
+ i = (maxregid * u) + v;
+ j = (maxregid * v) + u;
+ s->gbits[i/Sizetbits] |= 1ULL << (i % Sizetbits);
+ s->gbits[j/Sizetbits] |= 1ULL << (j % Sizetbits);
+ assert(gbhasedge(s, u, v) && gbhasedge(s, v, u));
+}
+
+static int wlfind(Loc **wl, size_t nwl, regid v, size_t *idx)
+{
+ size_t i;
+
+ for (i = 0; i < nwl; i++) {
+ if (wl[i]->reg.id == v) {
+ *idx = i;
+ return 1;
+ }
+ }
+ *idx = -1;
+ return 0;
+}
+
+/*
+ * If we have an edge between two aliasing registers,
+ * we should not increment the degree, since that would
+ * be double counting.
+ */
+static int degreechange(Isel *s, regid u, regid v)
+{
+ regid phys, virt, r;
+ size_t i;
+
+ if (bshas(s->prepainted, u)) {
+ phys = u;
+ virt = v;
+ } else if (bshas(s->prepainted, v)) {
+ phys = v;
+ virt = u;
+ } else {
+ return 1;
+ }
+
+ for (i = 0; i < Nmode; i++) {
+ r = regmap[colourmap[phys]][i];
+ if (r != phys && gbhasedge(s, virt, regmap[colourmap[phys]][i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void alputedge(Isel *s, regid u, regid v)
+{
+ s->ngadj[u]++;
+ s->gadj[u] = xrealloc(s->gadj[u], s->ngadj[u]*sizeof(regid));
+ s->gadj[u][s->ngadj[u] - 1] = v;
+}
+
+static void wlput(Loc ***wl, size_t *nwl, Loc *l)
+{
+ lappend(wl, nwl, l);
+ l->list = wl;
+}
+
+static void wldel(Isel *s, Loc ***wl, size_t *nwl, size_t idx)
+{
+ (*wl)[idx]->list = NULL;
+ ldel(wl, nwl, idx);
+}
+
+static void wlputset(Bitset *bs, regid r)
+{
+ bsput(bs, r);
+ locmap[r]->list = bs;
+}
+
+
+static void addedge(Isel *s, regid u, regid v)
+{
+ if (u == v || gbhasedge(s, u, v))
+ return;
+ if (u == Rrbp || u == Rrsp || u == Rrip)
+ return;
+ if (v == Rrbp || v == Rrsp || v == Rrip)
+ return;
+
+ gbputedge(s, u, v);
+ gbputedge(s, v, u);
+ if (!bshas(s->prepainted, u)) {
+ alputedge(s, u, v);
+ s->degree[u] += degreechange(s, v, u);
+ }
+ if (!bshas(s->prepainted, v)) {
+ alputedge(s, v, u);
+ s->degree[v] += degreechange(s, u, v);
+ }
+}
+
+static void setup(Isel *s)
+{
+ size_t gchunks;
+ size_t i;
+
+ free(s->gbits);
+ gchunks = (maxregid*maxregid)/Sizetbits + 1;
+ s->gbits = zalloc(gchunks*sizeof(size_t));
+ /* fresh adj list repr. */
+ free(s->gadj);
+ free(s->ngadj);
+ s->gadj = zalloc(maxregid * sizeof(regid*));
+ s->ngadj = zalloc(maxregid * sizeof(size_t));
+
+ s->spilled = bsclear(s->spilled);
+ s->coalesced = bsclear(s->coalesced);
+ lfree(&s->wlspill, &s->nwlspill);
+ lfree(&s->wlfreeze, &s->nwlfreeze);
+ lfree(&s->wlsimp, &s->nwlsimp);
+
+ free(s->aliasmap);
+ free(s->degree);
+ free(s->rmoves);
+ free(s->nrmoves);
+
+ s->aliasmap = zalloc(maxregid * sizeof(size_t));
+ s->degree = zalloc(maxregid * sizeof(int));
+ s->rmoves = zalloc(maxregid * sizeof(Loc **));
+ s->nrmoves = zalloc(maxregid * sizeof(size_t));
+
+ for (i = 0; bsiter(s->prepainted, &i); i++)
+ s->degree[i] = 1<<16;
+}
+
+static void build(Isel *s)
+{
+ regid u[Maxuse], d[Maxdef];
+ size_t nu, nd;
+ size_t i, k, a;
+ ssize_t j;
+ Bitset *live;
+ Asmbb **bb;
+ size_t nbb;
+ Insn *insn;
+ size_t l;
+
+ /* set up convenience vars */
+ bb = s->bb;
+ nbb = s->nbb;
+
+ for (i = 0; i < nbb; i++) {
+ live = bsdup(bb[i]->liveout);
+ for (j = bb[i]->ni - 1; j >= 0; j--) {
+ insn = bb[i]->il[j];
+ nu = uses(insn, u);
+ nd = defs(insn, d);
+
+ /* add these to the initial set */
+ for (k = 0; k < nu; k++) {
+ if (!bshas(s->prepainted, u[k]))
+ wlputset(s->initial, u[k]);
+ }
+ for (k = 0; k < nd; k++) {
+ if (!bshas(s->prepainted, d[k]))
+ wlputset(s->initial, d[k]);
+ }
+
+ /* moves get special treatment, since we don't want spurious
+ * edges between the src and dest */
+ //iprintf(stdout, insn);
+ if (ismove(insn)) {
+ /* live \= uses(i) */
+ for (k = 0; k < nu; k++) {
+ /* remove all physical register aliases */
+ if (bshas(s->prepainted, u[k])) {
+ for (a = 0; a < Nmode; a++)
+ bsdel(live, regmap[colourmap[u[k]]][a]);
+ } else {
+ bsdel(live, u[k]);
+ }
+ }
+
+ for (k = 0; k < nu; k++)
+ lappend(&s->rmoves[u[k]], &s->nrmoves[u[k]], insn);
+ for (k = 0; k < nd; k++)
+ lappend(&s->rmoves[d[k]], &s->nrmoves[d[k]], insn);
+ lappend(&s->wlmove, &s->nwlmove, insn);
+ }
+ /* live = live U def(i) */
+ for (k = 0; k < nd; k++)
+ bsput(live, d[k]);
+
+ for (k = 0; k < nd; k++)
+ for (l = 0; bsiter(live, &l); l++)
+ addedge(s, d[k], l);
+ /* live = use(i) U (live \ def(i)) */
+ for (k = 0; k < nd; k++)
+ bsdel(live, d[k]);
+ for (k = 0; k < nu; k++)
+ bsput(live, u[k]);
+ }
+ }
+}
+
+static int adjavail(Isel *s, regid r)
+{
+ if (bshas(s->coalesced, r))
+ return 0;
+ if (locmap[r]->list == &s->selstk)
+ return 0;
+ return 1;
+}
+
+static size_t nodemoves(Isel *s, regid n, Insn ***pil)
+{
+ size_t i, j;
+ size_t count;
+
+ /* FIXME: inefficient. Do I care? */
+ count = 0;
+ if (pil)
+ *pil = NULL;
+ for (i = 0; i < s->nrmoves[n]; i++) {
+ for (j = 0; j < s->nmactive; j++) {
+ if (s->mactive[j] == s->rmoves[n][i]) {
+ if (pil)
+ lappend(pil, &count, s->rmoves[n][i]);
+ else
+ count++;
+ }
+ }
+ for (j = 0; j < s->nwlmove; j++) {
+ if (s->wlmove[j] == s->rmoves[n][i]) {
+ if (pil)
+ lappend(pil, &count, s->rmoves[n][i]);
+ else
+ count++;
+ }
+ }
+ }
+ return count;
+}
+
+static int moverelated(Isel *s, regid n)
+{
+ return nodemoves(s, n, NULL) != 0;
+}
+
+static void mkworklist(Isel *s)
+{
+ size_t i;
+
+ for (i = 0; bsiter(s->initial, &i); i++) {
+ if (bshas(s->prepainted, i))
+ continue;
+ else if (!istrivial(s, i))
+ wlput(&s->wlspill, &s->nwlspill, locmap[i]);
+ else if (moverelated(s, i)) {
+ wlput(&s->wlfreeze, &s->nwlfreeze, locmap[i]);
+ }
+ else
+ wlput(&s->wlsimp, &s->nwlsimp, locmap[i]);
+ locmap[i]->reg.colour = 0;
+ }
+}
+
+static void enablemove(Isel *s, regid n)
+{
+ size_t i, j;
+ Insn **il;
+ size_t ni;
+
+ ni = nodemoves(s, n, &il);
+ for (i = 0; i < ni; i++) {
+ for (j = 0; j < s->nmactive; j++) {
+ if (il[i] == s->mactive[j]) {
+ ldel(&s->mactive, &s->nmactive, j);
+ lappend(&s->wlmove, &s->nwlmove, il[i]);
+ }
+ }
+ }
+}
+
+static void decdegree(Isel *s, regid m)
+{
+ int before, after;
+ int found;
+ size_t idx, i;
+ regid n;
+
+ assert(m < maxregid);
+ before = istrivial(s, m);
+ s->degree[m]--;
+ after = istrivial(s, m);
+
+ if (before != after) {
+ enablemove(s, m);
+ for (i = 0; i < s->ngadj[m]; i++) {
+ n = s->gadj[m][i];
+ if (adjavail(s, n))
+ enablemove(s, n);
+ }
+
+ /* Subtle:
+ *
+ * If this code is being called from coalesce(),
+ * then the degree could have been bumped up only
+ * temporarily. This means that the node can already
+ * be on wlfreeze or wlsimp.
+ *
+ * Therefore, if we don't find it on wlspill, we assert
+ * that the node is already on the list that we'd be
+ * moving it to.
+ */
+ found = wlfind(s->wlspill, s->nwlspill, m, &idx);
+ if (found)
+ wldel(s, &s->wlspill, &s->nwlspill, idx);
+ if (moverelated(s, m)) {
+ if (!found)
+ assert(wlfind(s->wlfreeze, s->nwlfreeze, m, &idx));
+ else
+ wlput(&s->wlfreeze, &s->nwlfreeze, locmap[m]);
+ } else {
+ if (!found)
+ assert(wlfind(s->wlsimp, s->nwlsimp, m, &idx));
+ else
+ wlput(&s->wlsimp, &s->nwlsimp, locmap[m]);
+ }
+ }
+}
+
+static void simp(Isel *s)
+{
+ Loc *l;
+ regid m;
+ size_t i;
+
+ l = lpop(&s->wlsimp, &s->nwlsimp);
+ wlput(&s->selstk, &s->nselstk, l);
+ for (i = 0; i < s->ngadj[l->reg.id]; i++) {
+ m = s->gadj[l->reg.id][i];
+ if (adjavail(s, m))
+ decdegree(s, m);
+ }
+}
+
+static regid getalias(Isel *s, regid id)
+{
+ while (1) {
+ if (!s->aliasmap[id])
+ break;
+ id = s->aliasmap[id]->reg.id;
+ };
+ return id;
+}
+
+static void wladd(Isel *s, regid u)
+{
+ size_t i;
+
+ if (bshas(s->prepainted, u))
+ return;
+ if (moverelated(s, u))
+ return;
+ if (!istrivial(s, u))
+ return;
+
+ assert(locmap[u]->list == &s->wlfreeze || locmap[u]->list == &s->wlsimp);
+ if (wlfind(s->wlfreeze, s->nwlfreeze, u, &i))
+ wldel(s, &s->wlfreeze, &s->nwlfreeze, i);
+ wlput(&s->wlsimp, &s->nwlsimp, locmap[u]);
+}
+
+static int conservative(Isel *s, regid u, regid v)
+{
+ int k;
+ size_t i;
+ regid n;
+
+ k = 0;
+ for (i = 0; i < s->ngadj[u]; i++) {
+ n = s->gadj[u][i];
+ if (adjavail(s, n) && !istrivial(s, n))
+ k++;
+ }
+ for (i = 0; i < s->ngadj[v]; i++) {
+ n = s->gadj[v][i];
+ if (adjavail(s, n) && !istrivial(s, n))
+ k++;
+ }
+ return k < _K[rclass(locmap[u])];
+}
+
+/* FIXME: is this actually correct? */
+static int ok(Isel *s, regid t, regid r)
+{
+ return istrivial(s, t) || bshas(s->prepainted, t) || gbhasedge(s, t, r);
+}
+
+static int combinable(Isel *s, regid u, regid v)
+{
+ regid t;
+ size_t i;
+
+ /* Regs of different modes can't be combined as things stand.
+ * In principle they should be combinable, but it confused the
+ * whole mode dance. */
+ if (locmap[u]->mode != locmap[v]->mode)
+ return 0;
+ /* if u isn't prepainted, can we conservatively coalesce? */
+ if (!bshas(s->prepainted, u) && conservative(s, u, v))
+ return 1;
+
+ /* if it is, are the adjacent nodes ok to combine with this? */
+ for (i = 0; i < s->ngadj[v]; i++) {
+ t = s->gadj[v][i];
+ if (adjavail(s, t) && !ok(s, t, u))
+ return 0;
+ }
+ return 1;
+}
+
+static void combine(Isel *s, regid u, regid v)
+{
+ regid t;
+ size_t idx;
+ size_t i, j;
+ int has;
+
+ if (debugopt['r'] > 2)
+ printedge(stdout, "combining:", u, v);
+ if (wlfind(s->wlfreeze, s->nwlfreeze, v, &idx))
+ wldel(s, &s->wlfreeze, &s->nwlfreeze, idx);
+ else if (wlfind(s->wlspill, s->nwlspill, v, &idx)) {
+ wldel(s, &s->wlspill, &s->nwlspill, idx);
+ }
+ wlputset(s->coalesced, v);
+ s->aliasmap[v] = locmap[u];
+
+ /* nodemoves[u] = nodemoves[u] U nodemoves[v] */
+ for (i = 0; i < s->nrmoves[v]; i++) {
+ has = 0;
+ for (j = 0; j < s->nrmoves[u]; j++) {
+ if (s->rmoves[v][i] == s->rmoves[u][j]) {
+ has = 1;
+ break;
+ }
+ }
+ if (!has)
+ lappend(&s->rmoves[u], &s->nrmoves[u], s->rmoves[v][i]);
+ }
+
+ for (i = 0; i < s->ngadj[v]; i++) {
+ t = s->gadj[v][i];
+ if (!adjavail(s, t))
+ continue;
+ if (debugopt['r'] > 2)
+ printedge(stdout, "combine-putedge:", t, u);
+ addedge(s, t, u);
+ decdegree(s, t);
+ }
+ if (!istrivial(s, u) && wlfind(s->wlfreeze, s->nwlfreeze, u, &idx)) {
+ wldel(s, &s->wlfreeze, &s->nwlfreeze, idx);
+ wlput(&s->wlspill, &s->nwlspill, locmap[u]);
+ }
+}
+
+static int constrained(Isel *s, regid u, regid v)
+{
+ size_t i;
+
+ if (bshas(s->prepainted, v))
+ return 1;
+ if (bshas(s->prepainted, u))
+ for (i = 0; i < Nmode; i++)
+ if (regmap[colourmap[u]][i] && gbhasedge(s, regmap[colourmap[u]][i], v))
+ return 1;
+ return gbhasedge(s, u, v);
+}
+
+static void coalesce(Isel *s)
+{
+ Insn *m;
+ regid u, v, tmp;
+
+ m = lpop(&s->wlmove, &s->nwlmove);
+ u = getalias(s, m->args[0]->reg.id);
+ v = getalias(s, m->args[1]->reg.id);
+
+ if (bshas(s->prepainted, v)) {
+ tmp = u;
+ u = v;
+ v = tmp;
+ }
+
+ if (u == v) {
+ lappend(&s->mcoalesced, &s->nmcoalesced, m);
+ wladd(s, u);
+ wladd(s, v);
+ } else if (constrained(s, u, v)) {
+ lappend(&s->mconstrained, &s->nmconstrained, m);
+ wladd(s, u);
+ wladd(s, v);
+ } else if (combinable(s, u, v)) {
+ lappend(&s->mcoalesced, &s->nmcoalesced, m);
+ combine(s, u, v);
+ wladd(s, u);
+ } else {
+ lappend(&s->mactive, &s->nmactive, m);
+ }
+}
+
+static int mldel(Insn ***ml, size_t *nml, Insn *m)
+{
+ size_t i;
+ for (i = 0; i < *nml; i++) {
+ if (m == (*ml)[i]) {
+ ldel(ml, nml, i);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void freezemoves(Isel *s, Loc *u)
+{
+ size_t i;
+ Insn **ml;
+ Insn *m;
+ size_t nml;
+ size_t idx;
+ Loc *v;
+
+ nml = nodemoves(s, u->reg.id, &ml);
+ for (i = 0; i < nml; i++) {
+ m = ml[i];
+ if (getalias(s, m->args[0]->reg.id) == getalias(s, u->reg.id))
+ v = locmap[getalias(s, m->args[1]->reg.id)];
+ else
+ v = locmap[getalias(s, m->args[0]->reg.id)];
+
+ if (!mldel(&s->mactive, &s->nmactive, m))
+ mldel(&s->wlmove, &s->nwlmove, m);
+ lappend(&s->mfrozen, &s->nmfrozen, m);
+ if (!nodemoves(s, v->reg.id, NULL) && istrivial(s, v->reg.id)) {
+ if (!wlfind(s->wlfreeze, s->nwlfreeze, v->reg.id, &idx))
+ die("Reg %zd not in freeze wl\n", v->reg.id);
+ wldel(s, &s->wlfreeze, &s->nwlfreeze, idx);
+ wlput(&s->wlsimp, &s->nwlsimp, v);
+ }
+
+ }
+ lfree(&ml, &nml);
+}
+
+static void freeze(Isel *s)
+{
+ Loc *l;
+
+ l = lpop(&s->wlfreeze, &s->nwlfreeze);
+ wlput(&s->wlsimp, &s->nwlsimp, l);
+ freezemoves(s, l);
+}
+
+/* Select the spill candidates */
+static void selspill(Isel *s)
+{
+ size_t i;
+ Loc *m;
+
+ /* FIXME: pick a better heuristic for spilling */
+ m = NULL;
+ for (i = 0; i < s->nwlspill; i++) {
+ if (!bshas(s->shouldspill, s->wlspill[i]->reg.id))
+ continue;
+ m = s->wlspill[i];
+ wldel(s, &s->wlspill, &s->nwlspill, i);
+ break;
+ }
+ if (!m) {
+ for (i = 0; i < s->nwlspill; i++) {
+ if (bshas(s->neverspill, s->wlspill[i]->reg.id)) {
+ printf("Not spilling %zd\n", s->wlspill[i]->reg.id);
+ continue;
+ }
+ m = s->wlspill[i];
+ wldel(s, &s->wlspill, &s->nwlspill, i);
+ break;
+ }
+ }
+ assert(m != NULL);
+ wlput(&s->wlsimp, &s->nwlsimp, m);
+ freezemoves(s, m);
+}
+
+/*
+ * Selects the colours for registers, spilling to the
+ * stack if no free registers can be found.
+ */
+static int paint(Isel *s)
+{
+ int taken[Nreg];
+ Loc *n, *w;
+ regid l;
+ size_t i, j;
+ int spilled;
+ int found;
+
+ spilled = 0;
+ while (s->nselstk) {
+ bzero(taken, Nreg*sizeof(int));
+ n = lpop(&s->selstk, &s->nselstk);
+
+ for (j = 0; j < s->ngadj[n->reg.id];j++) {
+ l = s->gadj[n->reg.id][j];
+ if (debugopt['r'] > 1)
+ printedge(stdout, "paint-edge:", n->reg.id, l);
+ w = locmap[getalias(s, l)];
+ if (w->reg.colour)
+ taken[colourmap[w->reg.colour]] = 1;
+ }
+
+ found = 0;
+ for (i = 0; i < Northogonal; i++) {
+ if (regmap[i][n->mode] && !taken[i]) {
+ if (debugopt['r']) {
+ fprintf(stdout, "\tselecting ");
+ locprint(stdout, n, 'x');
+ fprintf(stdout, " = %s\n", regnames[regmap[i][n->mode]]);
+ }
+ n->reg.colour = regmap[i][n->mode];
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ spilled = 1;
+ wlputset(s->spilled, n->reg.id);
+ }
+ }
+ for (l = 0; bsiter(s->coalesced, &l); l++) {
+ n = locmap[getalias(s, l)];
+ locmap[l]->reg.colour = n->reg.colour;
+ }
+ return spilled;
+}
+
+typedef struct Remapping Remapping;
+struct Remapping {
+ regid oldreg;
+ Loc *newreg;
+};
+
+static Loc *mapfind(Loc *old, Remapping *r, size_t nr)
+{
+ Loc *new;
+ Loc *base;
+ Loc *idx;
+ size_t i;
+
+ if (!old)
+ return NULL;
+
+ new = NULL;
+ if (old->type == Locreg) {
+ for (i = 0; i < nr; i++) {
+ if (old->reg.id == r[i].oldreg) {
+ return r[i].newreg;
+ }
+ }
+ } else if (old->type == Locmem || old->type == Locmeml) {
+ base = mapfind(old->mem.base, r, nr);
+ idx = mapfind(old->mem.idx, r, nr);
+ if (base != old->mem.base || idx != old->mem.idx) {
+ if (old->type == Locmem)
+ new = locmems(old->mem.constdisp, base, idx, old->mem.scale, old->mode);
+ else
+ new = locmemls(old->mem.lbldisp, base, idx, old->mem.scale, old->mode);
+ }
+ if (new)
+ return new;
+ }
+ return old;
+}
+
+static Loc *spillslot(Isel *s, regid reg)
+{
+ size_t stkoff;
+
+ stkoff = ptoi(htget(s->spillslots, itop(reg)));
+ return locmem(-stkoff, locphysreg(Rrbp), NULL, locmap[reg]->mode);
+}
+
+static void updatelocs(Isel *s, Insn *insn, Remapping *use, size_t nuse, Remapping *def, size_t ndef)
+{
+ size_t i;
+
+ for (i = 0; i < insn->nargs; i++) {
+ insn->args[i] = mapfind(insn->args[i], use, nuse);
+ insn->args[i] = mapfind(insn->args[i], def, ndef);
+ }
+}
+
+/*
+ * Takes two tables for remappings, of size Maxuse/Maxdef,
+ * and fills them, storign the number of uses or defs. Returns
+ * whether there are any remappings at all.
+ */
+static int remap(Isel *s, Insn *insn, Remapping *use, size_t *nuse, Remapping *def, size_t *ndef)
+{
+ regid u[Maxuse], d[Maxdef];
+ size_t nu, nd;
+ size_t useidx, defidx;
+ size_t i, j, k;
+ int found;
+
+ useidx = 0;
+ nu = uses(insn, u);
+ nd = defs(insn, d);
+ for (i = 0; i < nu; i++) {
+ if (!bshas(s->spilled, u[i]))
+ continue;
+ use[useidx].oldreg = u[i];
+ use[useidx].newreg = locreg(locmap[u[i]]->mode);
+ bsput(s->neverspill, use[useidx].newreg->reg.id);
+ useidx++;
+ }
+
+ defidx = 0;
+ for (i = 0; i < nd; i++) {
+ if (!bshas(s->spilled, d[i]))
+ continue;
+ def[defidx].oldreg = d[i];
+
+ /* if we already have remapped a use for this register, we want to
+ * store the same register from the def. */
+ found = 0;
+ for (j = 0; j < defidx; j++) {
+ for (k = 0; i < useidx; k++) {
+ if (use[j].oldreg == d[k]) {
+ def[defidx].newreg = use[j].newreg;
+ bsput(s->neverspill, def[defidx].newreg->reg.id);
+ found = 1;
+ }
+ }
+ }
+ if (!found) {
+ def[defidx].newreg = locreg(locmap[d[i]]->mode);
+ bsput(s->neverspill, def[defidx].newreg->reg.id);
+ }
+
+ defidx++;
+ }
+
+ *nuse = useidx;
+ *ndef = defidx;
+ return useidx > 0 || defidx > 0;
+}
+
+/*
+ * Rewrite instructions using spilled registers, inserting
+ * appropriate loads and stores into the BB
+ */
+static void rewritebb(Isel *s, Asmbb *bb)
+{
+ Remapping use[Maxuse], def[Maxdef];
+ Insn *insn;
+ size_t nuse, ndef;
+ size_t i, j;
+ Insn **new;
+ size_t nnew;
+
+ new = NULL;
+ nnew = 0;
+ for (j = 0; j < bb->ni; j++) {
+ /* if there is a remapping, insert the loads and stores as needed */
+ if (remap(s, bb->il[j], use, &nuse, def, &ndef)) {
+ for (i = 0; i < nuse; i++) {
+ insn = mkinsn(Imov, spillslot(s, use[i].oldreg), use[i].newreg, NULL);
+ lappend(&new, &nnew, insn);
+ if (debugopt['r']) {
+ printf("loading ");
+ locprint(stdout, locmap[use[i].oldreg], 'x');
+ printf(" -> ");
+ locprint(stdout, use[i].newreg, 'x');
+ printf("\n");
+ }
+ }
+ insn = bb->il[j];
+ updatelocs(s, insn, use, nuse, def, ndef);
+ lappend(&new, &nnew, insn);
+ for (i = 0; i < ndef; i++) {
+ insn = mkinsn(Imov, def[i].newreg, spillslot(s, def[i].oldreg), NULL);
+ lappend(&new, &nnew, insn);
+ if (debugopt['r']) {
+ printf("storing ");
+ locprint(stdout, locmap[def[i].oldreg], 'x');
+ printf(" -> ");
+ locprint(stdout, def[i].newreg, 'x');
+ printf("\n");
+ }
+ }
+ } else {
+ lappend(&new, &nnew, bb->il[j]);
+ }
+ }
+ lfree(&bb->il, &bb->ni);
+ bb->il = new;
+ bb->ni = nnew;
+}
+
+static void addspill(Isel *s, Loc *l)
+{
+ s->stksz->lit += modesize[l->mode];
+ s->stksz->lit = align(s->stksz->lit, modesize[l->mode]);
+ if (debugopt['r']) {
+ printf("spill ");
+ locprint(stdout, l, 'x');
+ printf(" to %zd(%%rbp)\n", s->stksz->lit);
+ }
+ htput(s->spillslots, itop(l->reg.id), itop(s->stksz->lit));
+}
+
+/*
+ * Rewrites the function code so that it no longer contains
+ * references to spilled registers. Every use of spilled regs
+ *
+ * insn %rX,%rY
+ *
+ * is rewritten to look like:
+ *
+ * mov 123(%rsp),%rZ
+ * insn %rZ,%rW
+ * mov %rW,234(%rsp)
+ */
+static void rewrite(Isel *s)
+{
+ size_t i;
+
+ s->spillslots = mkht(ptrhash, ptreq);
+ /* set up stack locations for all spilled registers. */
+ for (i = 0; bsiter(s->spilled, &i); i++)
+ addspill(s, locmap[i]);
+
+ /* rewrite instructions using them */
+ for (i = 0; i < s->nbb; i++)
+ rewritebb(s, s->bb[i]);
+ htfree(s->spillslots);
+ bsclear(s->spilled);
+}
+
+/*
+ * Coalescing registers leaves a lot
+ * of moves that look like
+ *
+ * mov %r123,%r123.
+ *
+ * This is useless. This deletes them.
+ */
+static void delnops(Isel *s)
+{
+ Insn *insn;
+ Asmbb *bb;
+ Insn **new;
+ size_t nnew;
+ size_t i, j;
+
+ for (i = 0; i < s->nbb; i++) {
+ new = NULL;
+ nnew = 0;
+ bb = s->bb[i];
+ for (j = 0; j < bb->ni; j++) {
+ insn = bb->il[j];
+ if (ismove(insn) && insn->args[0]->reg.colour == insn->args[1]->reg.colour)
+ continue;
+ lappend(&new, &nnew, insn);
+ }
+ lfree(&bb->il, &bb->ni);
+ bb->il = new;
+ bb->ni = nnew;
+ }
+ if (debugopt['r'])
+ dumpasm(s, stdout);
+}
+
+void regalloc(Isel *s)
+{
+ int spilled;
+ size_t i;
+
+ /* Initialize the list of prepainted registers */
+ s->prepainted = mkbs();
+ bsput(s->prepainted, 0);
+ for (i = 0; i < Nreg; i++)
+ bsput(s->prepainted, i);
+
+ s->shouldspill = mkbs();
+ s->neverspill = mkbs();
+ s->initial = mkbs();
+ for (i = 0; i < Nsaved; i++)
+ bsput(s->shouldspill, s->calleesave[i]->reg.id);
+ do {
+ setup(s);
+ liveness(s);
+ build(s);
+ mkworklist(s);
+ if (debugopt['r'])
+ dumpasm(s, stdout);
+ do {
+ if (s->nwlsimp)
+ simp(s);
+ else if (s->nwlmove)
+ coalesce(s);
+ else if (s->nwlfreeze)
+ freeze(s);
+ else if (s->nwlspill)
+ selspill(s);
+ } while (s->nwlsimp || s->nwlmove || s->nwlfreeze || s->nwlspill);
+ spilled = paint(s);
+ if (spilled)
+ rewrite(s);
+ } while (spilled);
+ delnops(s);
+ bsfree(s->prepainted);
+ bsfree(s->shouldspill);
+ bsfree(s->neverspill);
+}
+
+void wlprint(FILE *fd, char *name, Loc **wl, size_t nwl)
+{
+ size_t i;
+ char *sep;
+
+ sep = "";
+ fprintf(fd, "%s = [", name);
+ for (i = 0; i < nwl; i++) {
+ fprintf(fd, "%s", sep);
+ locprint(fd, wl[i], 'x');
+ fprintf(fd, "(%zd)", wl[i]->reg.id);
+ sep = ",";
+ }
+ fprintf(fd, "]\n");
+}
+
+static void setprint(FILE *fd, Bitset *s)
+{
+ char *sep;
+ size_t i;
+
+ sep = "";
+ for (i = 0; i < bsmax(s); i++) {
+ if (bshas(s, i)) {
+ fprintf(fd, "%s%zd", sep, i);
+ sep = ",";
+ }
+ }
+ fprintf(fd, "\n");
+}
+
+static void locsetprint(FILE *fd, Bitset *s)
+{
+ char *sep;
+ size_t i;
+
+ sep = "";
+ for (i = 0; i < bsmax(s); i++) {
+ if (bshas(s, i)) {
+ fprintf(fd, "%s", sep);
+ locprint(fd, locmap[i], 'x');
+ sep = ",";
+ }
+ }
+ fprintf(fd, "\n");
+}
+
+static void printedge(FILE *fd, char *msg, size_t a, size_t b)
+{
+ fprintf(fd, "\t%s ", msg);
+ locprint(fd, locmap[a], 'x');
+ fprintf(fd, " -- ");
+ locprint(fd, locmap[b], 'x');
+ fprintf(fd, "\n");
+}
+
+void dumpasm(Isel *s, FILE *fd)
+{
+ size_t i, j;
+ char *sep;
+ Asmbb *bb;
+
+ fprintf(fd, "WORKLISTS -- \n");
+ wlprint(stdout, "spill", s->wlspill, s->nwlspill);
+ wlprint(stdout, "simp", s->wlsimp, s->nwlsimp);
+ wlprint(stdout, "freeze", s->wlfreeze, s->nwlfreeze);
+ /* noisy to dump this all the time; only dump for higher debug levels */
+ if (debugopt['r'] > 2) {
+ fprintf(fd, "IGRAPH ----- \n");
+ for (i = 0; i < maxregid; i++) {
+ for (j = i; j < maxregid; j++) {
+ if (gbhasedge(s, i, j))
+ printedge(stdout, "", i, j);
+ }
+ }
+ }
+ fprintf(fd, "ASM -------- \n");
+ for (j = 0; j < s->nbb; j++) {
+ bb = s->bb[j];
+ fprintf(fd, "\n");
+ fprintf(fd, "Bb: %d labels=(", bb->id);
+ sep = "";
+ for (i = 0; i < bb->nlbls; i++) {;
+ fprintf(fd, "%s%s", bb->lbls[i], sep);
+ sep = ",";
+ }
+ fprintf(fd, ")\n");
+
+ fprintf(fd, "Pred: ");
+ setprint(fd, bb->pred);
+ fprintf(fd, "Succ: ");
+ setprint(fd, bb->succ);
+
+ fprintf(fd, "Use: ");
+ locsetprint(fd, bb->use);
+ fprintf(fd, "Def: ");
+ locsetprint(fd, bb->def);
+ fprintf(fd, "Livein: ");
+ locsetprint(fd, bb->livein);
+ fprintf(fd, "Liveout: ");
+ locsetprint(fd, bb->liveout);
+ for (i = 0; i < bb->ni; i++)
+ iprintf(fd, bb->il[i]);
+ }
+ fprintf(fd, "ENDASM -------- \n");
+}
+
--- /dev/null
+++ b/6/regs.def
@@ -1,0 +1,117 @@
+Reg(Rnone, "%NOREG", ModeB)
+/* byte regs */
+Reg(Ral, "%al", ModeB)
+Reg(Rcl, "%cl", ModeB)
+Reg(Rdl, "%dl", ModeB)
+Reg(Rbl, "%bl", ModeB)
+Reg(Rsil, "%sil", ModeB)
+Reg(Rdil, "%dil", ModeB)
+Reg(Rspl, "%spl", ModeB)
+Reg(Rbpl, "%bpl", ModeB)
+Reg(Rr8b, "%r8b", ModeB)
+Reg(Rr9b, "%r9b", ModeB)
+Reg(Rr10b, "%r10b", ModeB)
+Reg(Rr11b, "%r11b", ModeB)
+Reg(Rr12b, "%r12b", ModeB)
+Reg(Rr13b, "%r13b", ModeB)
+Reg(Rr14b, "%r14b", ModeB)
+Reg(Rr15b, "%r15b", ModeB)
+
+/* high byte regs. We *NEVER* allocate these */
+Reg(Rah, "%ah", ModeB)
+Reg(Rch, "%ch", ModeB)
+Reg(Rdh, "%dh", ModeB)
+Reg(Rbh, "%bh", ModeB)
+
+/* short regs */
+Reg(Rax, "%ax", ModeW)
+Reg(Rbx, "%bx", ModeW)
+Reg(Rcx, "%cx", ModeW)
+Reg(Rdx, "%dx", ModeW)
+Reg(Rsi, "%si", ModeW)
+Reg(Rdi, "%di", ModeW)
+Reg(Rsp, "%sp", ModeW)
+Reg(Rbp, "%bp", ModeW)
+Reg(Rr8w, "%r8w", ModeW)
+Reg(Rr9w, "%r9w", ModeW)
+Reg(Rr10w, "%r10w", ModeW)
+Reg(Rr11w, "%r11w", ModeW)
+Reg(Rr12w, "%r12w", ModeW)
+Reg(Rr13w, "%r13w", ModeW)
+Reg(Rr14w, "%r14w", ModeW)
+Reg(Rr15w, "%r15w", ModeW)
+
+
+/* long regs */
+Reg(Reax, "%eax", ModeL)
+Reg(Recx, "%ecx", ModeL)
+Reg(Redx, "%edx", ModeL)
+Reg(Rebx, "%ebx", ModeL)
+Reg(Resi, "%esi", ModeL)
+Reg(Redi, "%edi", ModeL)
+Reg(Resp, "%esp", ModeL)
+Reg(Rebp, "%ebp", ModeL)
+Reg(Rr8d, "%r8d", ModeL)
+Reg(Rr9d, "%r9d", ModeL)
+Reg(Rr10d, "%r10d", ModeL)
+Reg(Rr11d, "%r11d", ModeL)
+Reg(Rr12d, "%r12d", ModeL)
+Reg(Rr13d, "%r13d", ModeL)
+Reg(Rr14d, "%r14d", ModeL)
+Reg(Rr15d, "%r15d", ModeL)
+
+/* quad regs */
+Reg(Rrax, "%rax", ModeQ)
+Reg(Rrcx, "%rcx", ModeQ)
+Reg(Rrdx, "%rdx", ModeQ)
+Reg(Rrbx, "%rbx", ModeQ)
+Reg(Rrsi, "%rsi", ModeQ)
+Reg(Rrdi, "%rdi", ModeQ)
+Reg(Rr8, "%r8", ModeQ)
+Reg(Rr9, "%r9", ModeQ)
+Reg(Rr10, "%r10", ModeQ)
+Reg(Rr11, "%r11", ModeQ)
+Reg(Rr12, "%r12", ModeQ)
+Reg(Rr13, "%r13", ModeQ)
+Reg(Rr14, "%r14", ModeQ)
+Reg(Rr15, "%r15", ModeQ)
+
+/* floating point registers */
+Reg(Rxmm0f, "%xmm0", ModeF)
+Reg(Rxmm1f, "%xmm1", ModeF)
+Reg(Rxmm2f, "%xmm2", ModeF)
+Reg(Rxmm3f, "%xmm3", ModeF)
+Reg(Rxmm4f, "%xmm4", ModeF)
+Reg(Rxmm5f, "%xmm5", ModeF)
+Reg(Rxmm6f, "%xmm6", ModeF)
+Reg(Rxmm7f, "%xmm7", ModeF)
+Reg(Rxmm8f, "%xmm8", ModeF)
+Reg(Rxmm9f, "%xmm9", ModeF)
+Reg(Rxmm10f, "%xmm10", ModeF)
+Reg(Rxmm11f, "%xmm11", ModeF)
+Reg(Rxmm12f, "%xmm12", ModeF)
+Reg(Rxmm13f, "%xmm13", ModeF)
+Reg(Rxmm14f, "%xmm14", ModeF)
+Reg(Rxmm15f, "%xmm15", ModeF)
+
+/* double precision floating point registers */
+Reg(Rxmm0d, "%xmm0", ModeD)
+Reg(Rxmm1d, "%xmm1", ModeD)
+Reg(Rxmm2d, "%xmm2", ModeD)
+Reg(Rxmm3d, "%xmm3", ModeD)
+Reg(Rxmm4d, "%xmm4", ModeD)
+Reg(Rxmm5d, "%xmm5", ModeD)
+Reg(Rxmm6d, "%xmm6", ModeD)
+Reg(Rxmm7d, "%xmm7", ModeD)
+Reg(Rxmm8d, "%xmm8", ModeD)
+Reg(Rxmm9d, "%xmm9", ModeD)
+Reg(Rxmm10d, "%xmm10", ModeD)
+Reg(Rxmm11d, "%xmm11", ModeD)
+Reg(Rxmm12d, "%xmm12", ModeD)
+Reg(Rxmm13d, "%xmm13", ModeD)
+Reg(Rxmm14d, "%xmm14", ModeD)
+Reg(Rxmm15d, "%xmm15", ModeD)
+
+Reg(Rrip, "%rip", ModeQ)
+Reg(Rrsp, "%rsp", ModeQ)
+Reg(Rrbp, "%rbp", ModeQ)
--- /dev/null
+++ b/6/simp.c
@@ -1,0 +1,1836 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+#include "opt.h"
+#include "asm.h"
+
+#include "platform.h" /* HACK. We need some platform specific code gen behavior. *sigh.* */
+
+
+/* takes a list of nodes, and reduces it (and it's subnodes) to a list
+ * following these constraints:
+ * - All nodes are expression nodes
+ * - Nodes with side effects are root nodes
+ * - All nodes operate on machine-primitive types and tuples
+ */
+typedef struct Simp Simp;
+struct Simp {
+ int isglobl;
+
+ Node **stmts;
+ size_t nstmts;
+
+ /* return handling */
+ Node *endlbl;
+ Node *ret;
+ int isbigret;
+
+ /* pre/postinc handling */
+ Node **incqueue;
+ size_t nqueue;
+
+ /* break/continue handling */
+ Node **loopstep;
+ size_t nloopstep;
+ Node **loopexit;
+ size_t nloopexit;
+
+ /* location handling */
+ Node **blobs;
+ size_t nblobs;
+ size_t stksz;
+ size_t argsz;
+ Htab *globls;
+ Htab *stkoff;
+};
+
+static char *asmname(Node *n);
+static Node *simp(Simp *s, Node *n);
+static Node *rval(Simp *s, Node *n, Node *dst);
+static Node *lval(Simp *s, Node *n);
+static Node *assign(Simp *s, Node *lhs, Node *rhs);
+static void simpcond(Simp *s, Node *n, Node *ltrue, Node *lfalse);
+static void simpconstinit(Simp *s, Node *dcl);
+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 umatch(Simp *s, Node *pat, Node *val, Type *t, Node *iftrue, Node *iffalse);
+
+/* useful constants */
+static Type *tyintptr;
+static Type *tyword;
+static Type *tyvoid;
+
+size_t alignto(size_t sz, Type *t)
+{
+ size_t a;
+ size_t i;
+
+ t = tybase(t);
+ a = 0;
+ switch (t->type) {
+ case Tyarray:
+ a = alignto(1, t->sub[0]);
+ case Tytuple:
+ for (i = 0; i < t->nsub; i++)
+ a = max(alignto(1, t->sub[i]), a);
+ break;
+ case Tystruct:
+ for (i = 0; i < t->nmemb; i++)
+ a = max(alignto(1, decltype(t->sdecls[i])), a);
+ break;
+ default:
+ a = tysize(t);
+ break;
+ }
+
+ return align(sz, min(a, Ptrsz));
+}
+
+static Type *base(Type *t)
+{
+ assert(t->nsub == 1);
+ return t->sub[0];
+}
+
+static Node *add(Node *a, Node *b)
+{
+ Node *n;
+
+ assert(size(a) == size(b));
+ n = mkexpr(a->line, Oadd, a, b, NULL);
+ n->expr.type = a->expr.type;
+ return n;
+}
+
+static Node *addk(Node *n, uvlong v)
+{
+ Node *k;
+
+ k = mkintlit(n->line, v);
+ k->expr.type = exprtype(n);
+ return add(n, k);
+}
+
+static Node *sub(Node *a, Node *b)
+{
+ Node *n;
+
+ n = mkexpr(a->line, Osub, a, b, NULL);
+ n->expr.type = a->expr.type;
+ return n;
+}
+
+static Node *subk(Node *n, uvlong v)
+{
+ Node *k;
+
+ k = mkintlit(n->line, v);
+ k->expr.type = exprtype(n);
+ return sub(n, k);
+}
+
+static Node *mul(Node *a, Node *b)
+{
+ Node *n;
+
+ n = mkexpr(a->line, Omul, a, b, NULL);
+ n->expr.type = a->expr.type;
+ return n;
+}
+
+static int addressable(Simp *s, Node *a)
+{
+ if (a->type == Ndecl || (a->type == Nexpr && exprop(a) == Ovar))
+ return hthas(s->stkoff, a) || hthas(s->globls, a);
+ else
+ return stacknode(a);
+}
+
+int stacktype(Type *t)
+{
+ /* the types are arranged in types.def such that this is true */
+ t = tybase(t);
+ return t->type >= Tyslice;
+}
+
+int floattype(Type *t)
+{
+ t = tybase(t);
+ return t->type == Tyfloat32 || t->type == Tyfloat64;
+}
+
+int stacknode(Node *n)
+{
+ if (n->type == Nexpr)
+ return stacktype(n->expr.type);
+ else
+ return stacktype(n->decl.type);
+}
+
+int floatnode(Node *n)
+{
+ if (n->type == Nexpr)
+ return floattype(n->expr.type);
+ else
+ return floattype(n->decl.type);
+}
+
+static void forcelocal(Simp *s, Node *n)
+{
+ assert(n->type == Ndecl || (n->type == Nexpr && exprop(n) == Ovar));
+ s->stksz += size(n);
+ s->stksz = align(s->stksz, min(size(n), Ptrsz));
+ if (debugopt['i']) {
+ dump(n, stdout);
+ printf("declared at %zd, size = %zd\n", s->stksz, size(n));
+ }
+ htput(s->stkoff, n, itop(s->stksz));
+}
+
+static void declarelocal(Simp *s, Node *n)
+{
+ if (stacknode(n))
+ forcelocal(s, n);
+}
+
+/* takes the address of a node, possibly converting it to
+ * a pointer to the base type 'bt' */
+static Node *addr(Simp *s, Node *a, Type *bt)
+{
+ Node *n;
+
+ n = mkexpr(a->line, Oaddr, a, NULL);
+ if (!addressable(s, a))
+ forcelocal(s, a);
+ if (!bt)
+ n->expr.type = mktyptr(a->line, a->expr.type);
+ else
+ n->expr.type = mktyptr(a->line, bt);
+ return n;
+}
+
+static Node *load(Node *a)
+{
+ Node *n;
+
+ assert(a->expr.type->type == Typtr);
+ n = mkexpr(a->line, Oderef, a, NULL);
+ n->expr.type = base(a->expr.type);
+ return n;
+}
+
+static Node *deref(Node *a)
+{
+ Node *n;
+
+ assert(a->expr.type->type == Typtr);
+ n = mkexpr(a->line, Oderef, a, NULL);
+ n->expr.type = base(a->expr.type);
+ return n;
+}
+
+static Node *set(Node *a, Node *b)
+{
+ Node *n;
+
+ assert(a != NULL && b != NULL);
+ assert(exprop(a) == Ovar || exprop(a) == Oderef);
+ n = mkexpr(a->line, Oset, a, b, NULL);
+ n->expr.type = exprtype(a);
+ return n;
+}
+
+static Node *disp(int line, uint v)
+{
+ Node *n;
+
+ n = mkintlit(line, v);
+ n->expr.type = tyintptr;
+ return n;
+}
+
+static Node *word(int line, uint v)
+{
+ Node *n;
+
+ n = mkintlit(line, v);
+ n->expr.type = tyword;
+ return n;
+}
+
+static void append(Simp *s, Node *n)
+{
+ lappend(&s->stmts, &s->nstmts, n);
+}
+
+static int ispure(Node *n)
+{
+ return ispureop[exprop(n)];
+}
+
+static int isconstfn(Node *s)
+{
+ return s->decl.isconst && decltype(s)->type == Tyfunc;
+}
+
+/* For x86, the assembly names are generated as follows:
+ * local symbols: .name
+ * un-namespaced symbols: <symprefix>name
+ * namespaced symbols: <symprefix>namespace$name
+ */
+static char *asmname(Node *n)
+{
+ char *s;
+ int len;
+
+ len = strlen(Symprefix);
+ if (n->name.ns)
+ len += strlen(n->name.ns) + 1; /* +1 for separator */
+ len += strlen(n->name.name) + 1;
+
+ s = xalloc(len + 1);
+ s[0] = '\0';
+ if (n->name.ns)
+ snprintf(s, len, "%s%s$%s", Symprefix, n->name.ns, n->name.name);
+ else if (n->name.name[0] == '.')
+ snprintf(s, len, "%s", n->name.name);
+ else
+ snprintf(s, len, "%s%s", Symprefix, n->name.name);
+ return s;
+}
+
+size_t tysize(Type *t)
+{
+ size_t sz;
+ size_t i;
+
+ sz = 0;
+ if (!t)
+ die("size of empty type => bailing.");
+ switch (t->type) {
+ case Tyvoid:
+ die("void has no size");
+ return 1;
+ case Tybool: case Tyint8:
+ case Tybyte: case Tyuint8:
+ return 1;
+ case Tyint16: case Tyuint16:
+ return 2;
+ case Tyint: case Tyint32:
+ case Tyuint: case Tyuint32:
+ case Tychar: /* utf32 */
+ return 4;
+
+ case Typtr: case Tyfunc:
+ case Tyvalist: /* ptr to first element of valist */
+ return Ptrsz;
+
+ case Tyint64: case Tylong:
+ case Tyuint64: case Tyulong:
+ return 8;
+
+ /*end integer types*/
+ case Tyfloat32:
+ return 4;
+ case Tyfloat64:
+ return 8;
+
+ case Tyslice:
+ return 2*Ptrsz; /* len; ptr */
+ case Tyname:
+ return tysize(t->sub[0]);
+ case Tyarray:
+ t->asize = fold(t->asize, 1);
+ assert(exprop(t->asize) == Olit);
+ return t->asize->expr.args[0]->lit.intval * tysize(t->sub[0]);
+ case Tytuple:
+ for (i = 0; i < t->nsub; i++) {
+ sz = alignto(sz, t->sub[i]);
+ sz += tysize(t->sub[i]);
+ }
+ sz = alignto(sz, t);
+ return sz;
+ break;
+ case Tystruct:
+ for (i = 0; i < t->nmemb; i++) {
+ sz = alignto(sz, decltype(t->sdecls[i]));
+ sz += size(t->sdecls[i]);
+ }
+ sz = alignto(sz, t);
+ return sz;
+ break;
+ case Tyunion:
+ sz = Wordsz;
+ for (i = 0; i < t->nmemb; i++)
+ if (t->udecls[i]->etype)
+ sz = max(sz, tysize(t->udecls[i]->etype) + Wordsz);
+ return align(sz, Ptrsz);
+ break;
+ case Tybad: case Tyvar: case Typaram: case Tyunres: case Ntypes:
+ die("Type %s does not have size; why did it get down to here?", tystr(t));
+ break;
+ }
+ return -1;
+}
+
+size_t size(Node *n)
+{
+ Type *t;
+
+ if (n->type == Nexpr)
+ t = n->expr.type;
+ else
+ t = n->decl.type;
+ return tysize(t);
+}
+
+static Node *gentemp(Simp *simp, Node *e, Type *ty, Node **dcl)
+{
+ char buf[128];
+ static int nexttmp;
+ Node *t, *r, *n;
+
+ snprintf(buf, 128, ".t%d", nexttmp++);
+ n = mkname(e->line, buf);
+ t = mkdecl(e->line, n, ty);
+ r = mkexpr(e->line, 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;
+
+ assert(e->type == Nexpr);
+ t = gentemp(simp, e, e->expr.type, &dcl);
+ if (stacknode(e))
+ declarelocal(simp, dcl);
+ return t;
+}
+
+static void jmp(Simp *s, Node *lbl)
+{
+ append(s, mkexpr(lbl->line, Ojmp, lbl, NULL));
+}
+
+static void cjmp(Simp *s, Node *cond, Node *iftrue, Node *iffalse)
+{
+ Node *jmp;
+
+ jmp = mkexpr(cond->line, Ocjmp, cond, iftrue, iffalse, NULL);
+ append(s, jmp);
+}
+
+static Node *slicelen(Simp *s, Node *sl)
+{
+ /* *(&sl + sizeof(size_t)) */
+ return load(addk(addr(s, sl, tyintptr), Ptrsz));
+}
+
+
+static Node *seqlen(Simp *s, Node *n, Type *ty)
+{
+ Node *t, *r;
+
+ if (exprtype(n)->type == Tyslice) {
+ t = slicelen(s, n);
+ r = simpcast(s, t, ty);
+ } else if (exprtype(n)->type == Tyarray) {
+ t = exprtype(n)->asize;
+ r = simpcast(s, t, ty);
+ } else {
+ r = NULL;
+ }
+ return r;
+}
+
+/* if foo; bar; else baz;;
+ * => cjmp (foo) :bar :baz */
+static void simpif(Simp *s, Node *n, Node *exit)
+{
+ Node *l1, *l2, *l3;
+ Node *iftrue, *iffalse;
+
+ l1 = genlbl();
+ l2 = genlbl();
+ if (exit)
+ l3 = exit;
+ else
+ l3 = genlbl();
+
+ iftrue = n->ifstmt.iftrue;
+ iffalse = n->ifstmt.iffalse;
+
+ simpcond(s, n->ifstmt.cond, l1, l2);
+ simp(s, l1);
+ simp(s, iftrue);
+ jmp(s, l3);
+ simp(s, l2);
+ /* because lots of bunched up end labels are ugly,
+ * coalesce them by handling 'elif'-like constructs
+ * separately */
+ if (iffalse && iffalse->type == Nifstmt) {
+ simpif(s, iffalse, exit);
+ } else {
+ simp(s, iffalse);
+ jmp(s, l3);
+ }
+
+ if (!exit)
+ simp(s, l3);
+}
+
+/* init; while cond; body;;
+ * => init
+ * jmp :cond
+ * :body
+ * ...body...
+ * ...step...
+ * :cond
+ * ...cond...
+ * cjmp (cond) :body :end
+ * :end
+ */
+static void simploop(Simp *s, Node *n)
+{
+ Node *lbody;
+ Node *lend;
+ Node *lcond;
+ Node *lstep;
+
+ lbody = genlbl();
+ lcond = genlbl();
+ lstep = genlbl();
+ lend = genlbl();
+
+ lappend(&s->loopstep, &s->nloopstep, lstep);
+ lappend(&s->loopexit, &s->nloopexit, lend);
+
+ simp(s, n->loopstmt.init); /* init */
+ jmp(s, lcond); /* goto test */
+ simp(s, lbody); /* body lbl */
+ simp(s, n->loopstmt.body); /* body */
+ simp(s, lstep); /* test lbl */
+ simp(s, n->loopstmt.step); /* step */
+ simp(s, lcond); /* test lbl */
+ simpcond(s, n->loopstmt.cond, lbody, lend); /* repeat? */
+ simp(s, lend); /* exit */
+
+ s->nloopstep--;
+ s->nloopexit--;
+}
+
+/* pat; seq;
+ * body;;
+ *
+ * =>
+ * .pseudo = seqinit
+ * jmp :cond
+ * :body
+ * ...body...
+ * :step
+ * ...step...
+ * :cond
+ * ...cond...
+ * cjmp (cond) :match :end
+ * :match
+ * ...match...
+ * cjmp (match) :body :step
+ * :end
+ */
+static void simpiter(Simp *s, Node *n)
+{
+ Node *lbody, *lstep, *lcond, *lmatch, *lend;
+ Node *idx, *len, *dcl, *seq, *val, *done;
+ Node *zero;
+
+ lbody = genlbl();
+ lstep = genlbl();
+ lcond = genlbl();
+ lmatch = genlbl();
+ lend = genlbl();
+
+ lappend(&s->loopstep, &s->nloopstep, lstep);
+ lappend(&s->loopexit, &s->nloopexit, lend);
+
+ zero = mkintlit(n->line, 0);
+ zero->expr.type = tyintptr;
+
+ seq = rval(s, n->iterstmt.seq, NULL);
+ idx = gentemp(s, n, tyintptr, &dcl);
+ declarelocal(s, dcl);
+
+ /* setup */
+ append(s, assign(s, idx, zero));
+ jmp(s, lcond);
+ simp(s, lbody);
+ /* body */
+ simp(s, n->iterstmt.body);
+ /* step */
+ simp(s, lstep);
+ simp(s, assign(s, idx, addk(idx, 1)));
+ /* condition */
+ simp(s, lcond);
+ len = seqlen(s, seq, tyintptr);
+ done = mkexpr(n->line, Olt, idx, len, NULL);
+ cjmp(s, done, lmatch, lend);
+ simp(s, lmatch);
+ val = load(idxaddr(s, seq, idx));
+ umatch(s, n->iterstmt.elt, val, val->expr.type, lbody, lstep);
+ simp(s, lend);
+
+ s->nloopstep--;
+ s->nloopexit--;
+}
+
+static Ucon *finducon(Node *n)
+{
+ size_t i;
+ Type *t;
+ Ucon *uc;
+
+ t = tybase(n->expr.type);
+ if (exprop(n) != Oucon)
+ return NULL;
+ for (i = 0; i < t->nmemb; i++) {
+ uc = t->udecls[i];
+ if (!strcmp(namestr(uc->name), namestr(n->expr.args[0])))
+ return uc;
+ }
+ die("No ucon?!?");
+ return NULL;
+}
+
+static Node *uconid(Simp *s, Node *n)
+{
+ Ucon *uc;
+
+ if (exprop(n) != Oucon)
+ return load(addr(s, n, mktype(n->line, Tyuint)));
+
+ uc = finducon(n);
+ return word(uc->line, 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 umatch(Simp *s, Node *pat, Node *val, Type *t, Node *iftrue, Node *iffalse)
+{
+ Node *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 && !decls[pat->expr.did]->decl.isconst) {
+ v = assign(s, pat, val);
+ append(s, v);
+ 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:
+ die("Unsupported type for pattern");
+ break;
+ /* only valid for string literals */
+ case Tyslice:
+ lit = pat->expr.args[0];
+ if (exprop(pat) != Olit || lit->lit.littype != Lstr)
+ die("Unsupported pattern");
+ str = lit->lit.strval;
+
+ /* load slice length */
+ next = genlbl();
+ x = slicelen(s, val);
+ len = strlen(str);
+ y = mkintlit(lit->line, len);
+ y->expr.type = tyintptr;
+ v = mkexpr(pat->line, Oeq, x, y, NULL);
+ cjmp(s, v, next, iffalse);
+ append(s, next);
+
+ for (i = 0; i < len; i++) {
+ next = genlbl();
+ x = mkintlit(pat->line, str[i]);
+ x->expr.type = mktype(-1, Tybyte);
+ idx = mkintlit(pat->line, i);
+ idx->expr.type = tyintptr;
+ y = load(idxaddr(s, val, idx));
+ v = mkexpr(pat->line, Oeq, x, y, NULL);
+ v->expr.type = mktype(pat->line, Tybool);
+ cjmp(s, v, next, iffalse);
+ append(s, next);
+ }
+ jmp(s, iftrue);
+ break;
+ 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 Tylong: case Tyulong:
+ case Tyfloat32: case Tyfloat64:
+ case Typtr: case Tyfunc:
+ v = mkexpr(pat->line, Oeq, pat, val, NULL);
+ v->expr.type = mktype(pat->line, Tybool);
+ cjmp(s, v, iftrue, iffalse);
+ 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 Tystruct: 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();
+ v = load(addk(addr(s, val, exprtype(patarg[i])), off));
+ umatch(s, patarg[i], v, exprtype(patarg[i]), next, iffalse);
+ append(s, next);
+ off += size(patarg[i]);
+ }
+ jmp(s, iftrue);
+ break;
+ case Tyunion:
+ uc = finducon(pat);
+ if (!uc)
+ uc = finducon(val);
+
+ deeper = genlbl();
+
+ x = uconid(s, pat);
+ y = uconid(s, val);
+ v = mkexpr(pat->line, 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);
+ umatch(s, pat, val, uc->etype, iftrue, iffalse);
+ }
+ break;
+ }
+}
+
+static void simpmatch(Simp *s, Node *n)
+{
+ Node *end, *cur, *next; /* labels */
+ Node *val, *tmp;
+ Node *m;
+ size_t i;
+
+ end = genlbl();
+ val = temp(s, n->matchstmt.val);
+ tmp = rval(s, n->matchstmt.val, val);
+ if (val != tmp)
+ append(s, assign(s, val, tmp));
+ for (i = 0; i < n->matchstmt.nmatches; i++) {
+ m = n->matchstmt.matches[i];
+
+ /* check pattern */
+ cur = genlbl();
+ next = genlbl();
+ umatch(s, m->match.pat, val, val->expr.type, cur, next);
+
+ /* do the action if it matches */
+ append(s, cur);
+ simp(s, m->match.block);
+ jmp(s, end);
+ append(s, next);
+ }
+ append(s, end);
+}
+
+static void simpblk(Simp *s, Node *n)
+{
+ size_t i;
+
+ pushstab(n->block.scope);
+ for (i = 0; i < n->block.nstmts; i++) {
+ n->block.stmts[i] = fold(n->block.stmts[i], 0);
+ simp(s, n->block.stmts[i]);
+ }
+ popstab();
+}
+
+static Node *simpblob(Simp *s, Node *blob, Node ***l, size_t *nl)
+{
+ Node *n, *d, *r;
+ char lbl[128];
+
+ n = mkname(blob->line, genlblstr(lbl, 128));
+ d = mkdecl(blob->line, n, blob->expr.type);
+ r = mkexpr(blob->line, Ovar, n, NULL);
+
+ d->decl.init = blob;
+ d->decl.type = blob->expr.type;
+ d->decl.isconst = 1;
+ htput(s->globls, d, strdup(lbl));
+
+ r->expr.did = d->decl.did;
+ r->expr.type = blob->expr.type;
+ r->expr.isconst = 1;
+
+ lappend(l, nl, d);
+ return r;
+}
+
+/* gets the byte offset of 'memb' within the aggregate type 'aggr' */
+static size_t offset(Node *aggr, Node *memb)
+{
+ Type *ty;
+ size_t i;
+ size_t off;
+
+ ty = tybase(exprtype(aggr));
+ if (ty->type == Typtr)
+ ty = tybase(ty->sub[0]);
+
+ assert(ty->type == Tystruct);
+ off = 0;
+ for (i = 0; i < ty->nmemb; i++) {
+ off = alignto(off, decltype(ty->sdecls[i]));
+ if (!strcmp(namestr(memb), declname(ty->sdecls[i])))
+ return off;
+ off += size(ty->sdecls[i]);
+ }
+ die("Could not find member %s in struct", namestr(memb));
+ return -1;
+}
+
+static Node *ptrsized(Simp *s, Node *v)
+{
+ if (size(v) == Ptrsz)
+ return v;
+ else if (size(v) < Ptrsz)
+ v = mkexpr(v->line, Ozwiden, v, NULL);
+ else if (size(v) > Ptrsz)
+ v = mkexpr(v->line, Otrunc, v, NULL);
+ v->expr.type = tyintptr;
+ return v;
+}
+
+static Node *membaddr(Simp *s, Node *n)
+{
+ Node *t, *u, *r;
+ Node **args;
+ Type *ty;
+
+ args = n->expr.args;
+ ty = tybase(exprtype(args[0]));
+ if (ty->type == Typtr) {
+ t = lval(s, args[0]);
+ } else {
+ t = addr(s, lval(s, args[0]), exprtype(n));
+ }
+ u = disp(n->line, offset(args[0], args[1]));
+ r = add(t, u);
+ r->expr.type = mktyptr(n->line, n->expr.type);
+ return r;
+}
+
+static Node *idxaddr(Simp *s, Node *seq, Node *idx)
+{
+ Node *a, *t, *u, *v; /* temps */
+ Node *r; /* result */
+ Type *ty;
+ size_t sz;
+
+ a = rval(s, seq, NULL);
+ ty = exprtype(seq)->sub[0];
+ if (exprtype(seq)->type == Tyarray)
+ t = addr(s, a, ty);
+ else if (seq->expr.type->type == Tyslice)
+ t = load(addr(s, a, mktyptr(seq->line, ty)));
+ else
+ die("Can't index type %s\n", tystr(seq->expr.type));
+ assert(t->expr.type->type == Typtr);
+ u = rval(s, idx, NULL);
+ u = ptrsized(s, u);
+ sz = tysize(ty);
+ v = mul(u, disp(seq->line, sz));
+ r = add(t, v);
+ return r;
+}
+
+static Node *slicebase(Simp *s, Node *n, Node *off)
+{
+ Node *t, *u, *v;
+ Type *ty;
+ int sz;
+
+ t = rval(s, n, NULL);
+ u = NULL;
+ ty = tybase(exprtype(n));
+ switch (ty->type) {
+ case Typtr: u = t; break;
+ case Tyarray: u = addr(s, t, base(exprtype(n))); break;
+ case Tyslice: u = load(addr(s, t, mktyptr(n->line, base(exprtype(n))))); break;
+ default: die("Unslicable type %s", tystr(n->expr.type));
+ }
+ /* safe: all types we allow here have a sub[0] that we want to grab */
+ if (off) {
+ off = ptrsized(s, rval(s, off, NULL));
+ sz = tysize(n->expr.type->sub[0]);
+ v = mul(off, disp(n->line, sz));
+ return add(u, v);
+ } else {
+ return u;
+ }
+}
+
+static Node *lval(Simp *s, Node *n)
+{
+ Node *r;
+
+ switch (exprop(n)) {
+ case Ovar: r = n; break;
+ case Oidx: r = deref(idxaddr(s, n->expr.args[0], n->expr.args[1])); break;
+ case Oderef: r = deref(rval(s, n->expr.args[0], NULL)); break;
+ case Omemb: r = deref(membaddr(s, n)); break;
+ default:
+ die("%s cannot be an lval", opstr(exprop(n)));
+ break;
+ }
+ return r;
+}
+
+static void simpcond(Simp *s, Node *n, Node *ltrue, Node *lfalse)
+{
+ Node **args;
+ Node *v, *lnext;
+
+ args = n->expr.args;
+ switch (exprop(n)) {
+ case Oland:
+ lnext = genlbl();
+ simpcond(s, args[0], lnext, lfalse);
+ append(s, lnext);
+ simpcond(s, args[1], ltrue, lfalse);
+ break;
+ case Olor:
+ lnext = genlbl();
+ simpcond(s, args[0], ltrue, lnext);
+ append(s, lnext);
+ simpcond(s, args[1], ltrue, lfalse);
+ break;
+ case Olnot:
+ simpcond(s, args[0], lfalse, ltrue);
+ break;
+ default:
+ v = rval(s, n, NULL);
+ cjmp(s, v, ltrue, lfalse);
+ break;
+ }
+}
+
+static Node *intconvert(Simp *s, Node *from, Type *to, int issigned)
+{
+ Node *r;
+ size_t fromsz, tosz;
+
+ fromsz = size(from);
+ tosz = tysize(to);
+ r = rval(s, from, NULL);
+ if (fromsz > tosz) {
+ r = mkexpr(from->line, Otrunc, r, NULL);
+ } else if (tosz > fromsz) {
+ if (issigned)
+ r = mkexpr(from->line, Oswiden, r, NULL);
+ else
+ r = mkexpr(from->line, Ozwiden, r, NULL);
+ }
+ r->expr.type = to;
+ return r;
+}
+
+static Node *simpcast(Simp *s, Node *val, Type *to)
+{
+ Node *r;
+ Type *t;
+
+ r = NULL;
+ /* do the type conversion */
+ switch (tybase(to)->type) {
+ case Tybool:
+ case Tyint8: case Tyint16: case Tyint32: case Tyint64:
+ case Tyuint8: case Tyuint16: case Tyuint32: case Tyuint64:
+ case Tyint: case Tyuint: case Tylong: case Tyulong:
+ case Tychar: case Tybyte:
+ case Typtr:
+ t = tybase(exprtype(val));
+ switch (t->type) {
+ /* ptr -> slice conversion is disallowed */
+ case Tyslice:
+ if (t->type == Typtr)
+ fatal(val->line, "Bad cast from %s to %s",
+ tystr(exprtype(val)), tystr(to));
+ r = slicebase(s, val, NULL);
+ break;
+ /* signed conversions */
+ case Tyint8: case Tyint16: case Tyint32: case Tyint64:
+ case Tyint: case Tylong:
+ r = intconvert(s, val, to, 1);
+ break;
+ /* unsigned conversions */
+ case Tybool:
+ case Tyuint8: case Tyuint16: case Tyuint32: case Tyuint64:
+ case Tyuint: case Tyulong: case Tychar: case Tybyte:
+ case Typtr:
+ r = intconvert(s, val, to, 0);
+ break;
+ case Tyfloat32: case Tyfloat64:
+ if (tybase(to)->type == Typtr)
+ fatal(val->line, "Bad cast from %s to %s",
+ tystr(exprtype(val)), tystr(to));
+ r = mkexpr(val->line, Oflt2int, rval(s, val, NULL), NULL);
+ r->expr.type = to;
+ break;
+ default:
+ fatal(val->line, "Bad cast from %s to %s",
+ tystr(exprtype(val)), tystr(to));
+ }
+ break;
+ case Tyfloat32: case Tyfloat64:
+ t = tybase(exprtype(val));
+ switch (t->type) {
+ case Tyint8: case Tyint16: case Tyint32: case Tyint64:
+ case Tyuint8: case Tyuint16: case Tyuint32: case Tyuint64:
+ case Tyint: case Tyuint: case Tylong: case Tyulong:
+ case Tychar: case Tybyte:
+ r = mkexpr(val->line, Oflt2int, rval(s, val, NULL), NULL);
+ r->expr.type = to;
+ break;
+ default:
+ fatal(val->line, "Bad cast from %s to %s",
+ tystr(exprtype(val)), tystr(to));
+ break;
+ }
+ break;
+ /* no other destination types are handled as things stand */
+ default:
+ fatal(val->line, "Bad cast from %s to %s",
+ tystr(exprtype(val)), tystr(to));
+ }
+ return r;
+}
+
+/* Simplifies taking a slice of an array, pointer,
+ * or other slice down to primitive pointer operations */
+static Node *simpslice(Simp *s, Node *n, Node *dst)
+{
+ Node *t;
+ Node *start, *end;
+ Node *base, *sz, *len;
+ Node *stbase, *stlen;
+
+ if (dst)
+ t = dst;
+ else
+ t = temp(s, n);
+ /* *(&slice) = (void*)base + off*sz */
+ base = slicebase(s, n->expr.args[0], n->expr.args[1]);
+ start = ptrsized(s, rval(s, n->expr.args[1], NULL));
+ end = ptrsized(s, rval(s, n->expr.args[2], NULL));
+ len = sub(end, start);
+ /* we can be storing through a pointer, in the case
+ * of '*foo = bar'. */
+ if (tybase(exprtype(t))->type == Typtr) {
+ stbase = set(simpcast(s, t, mktyptr(t->line, tyintptr)), base);
+ sz = addk(simpcast(s, t, mktyptr(t->line, tyintptr)), Ptrsz);
+ } else {
+ stbase = set(deref(addr(s, t, tyintptr)), base);
+ sz = addk(addr(s, t, tyintptr), Ptrsz);
+ }
+ /* *(&slice + ptrsz) = len */
+ stlen = set(deref(sz), len);
+ append(s, stbase);
+ append(s, stlen);
+ return t;
+}
+
+static Node *visit(Simp *s, Node *n)
+{
+ size_t i;
+ Node *r;
+
+ for (i = 0; i < n->expr.nargs; i++)
+ n->expr.args[i] = rval(s, n->expr.args[i], NULL);
+ if (ispure(n)) {
+ r = n;
+ } else {
+ if (exprtype(n)->type == Tyvoid) {
+ r = NULL;
+ append(s, n);
+ } else {
+ r = temp(s, n);
+ append(s, set(r, n));
+ }
+ }
+ return r;
+}
+
+/* Takes a tuple and binds the i'th element of it to the
+ * i'th name on the rhs of the assignment. */
+static Node *destructure(Simp *s, Node *lhs, Node *rhs)
+{
+ Node *plv, *prv, *lv, *sz, *stor, **args;
+ size_t off, i;
+
+ args = lhs->expr.args;
+ rhs = rval(s, rhs, NULL);
+ off = 0;
+ for (i = 0; i < lhs->expr.nargs; i++) {
+ lv = lval(s, args[i]);
+ off = alignto(off, exprtype(lv));
+ prv = add(addr(s, rhs, exprtype(args[i])), disp(rhs->line, off));
+ if (stacknode(args[i])) {
+ sz = disp(lhs->line, size(lv));
+ plv = addr(s, lv, exprtype(lv));
+ stor = mkexpr(lhs->line, Oblit, plv, prv, sz, NULL);
+ } else {
+ stor = set(lv, load(prv));
+ }
+ append(s, stor);
+ off += size(lv);
+ }
+
+ return NULL;
+}
+
+static Node *assign(Simp *s, Node *lhs, Node *rhs)
+{
+ Node *t, *u, *v, *r;
+
+ if (exprop(lhs) == Otup) {
+ r = destructure(s, lhs, rhs);
+ } else {
+ t = lval(s, lhs);
+ u = rval(s, rhs, t);
+
+ /* if we stored the result into t, rval() should return that,
+ * so we know our work is done. */
+ if (u == t) {
+ r = t;
+ } else if (stacknode(lhs)) {
+ t = addr(s, t, exprtype(lhs));
+ u = addr(s, u, exprtype(lhs));
+ v = disp(lhs->line, size(lhs));
+ r = mkexpr(lhs->line, Oblit, t, u, v, NULL);
+ } else {
+ r = set(t, u);
+ }
+ }
+ return r;
+}
+
+static Node *assignat(Simp *s, Node *r, size_t off, Node *val)
+{
+ Node *pval, *pdst;
+ Node *sz;
+ Node *st;
+
+ val = rval(s, val, NULL);
+ pdst = add(r, disp(val->line, off));
+
+ if (stacknode(val)) {
+ sz = disp(val->line, size(val));
+ pval = addr(s, val, exprtype(val));
+ st = mkexpr(val->line, Oblit, pdst, pval, sz, NULL);
+ } else {
+ st = set(deref(pdst), val);
+ }
+ append(s, st);
+ return r;
+}
+
+/* Simplify tuple construction to a stack allocated
+ * value by evaluating the rvalue of each node on the
+ * rhs and assigning it to the correct offset from the
+ * head of the tuple. */
+static Node *simptup(Simp *s, Node *n, Node *dst)
+{
+ Node **args;
+ Node *r;
+ size_t i, off;
+
+ args = n->expr.args;
+ if (!dst)
+ dst = temp(s, n);
+ r = addr(s, dst, exprtype(dst));
+
+ off = 0;
+ for (i = 0; i < n->expr.nargs; i++) {
+ off = alignto(off, exprtype(args[i]));
+ assignat(s, r, off, args[i]);
+ off += size(args[i]);
+ }
+ return dst;
+}
+
+static Node *simpucon(Simp *s, Node *n, Node *dst)
+{
+ Node *tmp, *u, *tag, *elt, *sz;
+ Node *r;
+ Type *ty;
+ Ucon *uc;
+ size_t i;
+
+ /* find the ucon we're constructing here */
+ ty = tybase(n->expr.type);
+ uc = NULL;
+ for (i = 0; i < ty->nmemb; i++) {
+ if (!strcmp(namestr(n->expr.args[0]), namestr(ty->udecls[i]->name))) {
+ uc = ty->udecls[i];
+ break;
+ }
+ }
+ if (!uc)
+ die("Couldn't find union constructor");
+
+ if (dst)
+ tmp = dst;
+ else
+ tmp = temp(s, n);
+
+ /* Set the tag on the ucon */
+ u = addr(s, tmp, mktype(n->line, Tyuint));
+ tag = mkintlit(n->line, uc->id);
+ tag->expr.type = mktype(n->line, Tyuint);
+ append(s, set(deref(u), tag));
+
+
+ /* fill the value, if needed */
+ if (!uc->etype)
+ return tmp;
+ elt = rval(s, n->expr.args[1], NULL);
+ u = addk(u, Wordsz);
+ if (stacktype(uc->etype)) {
+ elt = addr(s, elt, uc->etype);
+ sz = disp(n->line, tysize(uc->etype));
+ r = mkexpr(n->line, Oblit, u, elt, sz, NULL);
+ } else {
+ r = set(deref(u), elt);
+ }
+ append(s, r);
+ return tmp;
+}
+
+static Node *simpuget(Simp *s, Node *n, Node *dst)
+{
+ die("No uget simplification yet");
+}
+
+/* simplifies
+ * a || b
+ * to
+ * if a || b
+ * t = true
+ * else
+ * t = false
+ * ;;
+ */
+static Node *simplazy(Simp *s, Node *n)
+{
+ Node *r, *t, *u;
+ Node *ltrue, *lfalse, *ldone;
+
+ /* set up temps and labels */
+ r = temp(s, n);
+ ltrue = genlbl();
+ lfalse = genlbl();
+ ldone = genlbl();
+
+ /* simp the conditional */
+ simpcond(s, n, ltrue, lfalse);
+
+ /* if true */
+ append(s, ltrue);
+ u = mkexpr(n->line, Olit, mkbool(n->line, 1), NULL);
+ u->expr.type = mktype(n->line, Tybool);
+ t = set(r, u);
+ append(s, t);
+ jmp(s, ldone);
+
+ /* if false */
+ append(s, lfalse);
+ u = mkexpr(n->line, Olit, mkbool(n->line, 0), NULL);
+ u->expr.type = mktype(n->line, Tybool);
+ t = set(r, u);
+ append(s, t);
+ jmp(s, ldone);
+
+ /* finish */
+ append(s, ldone);
+ return r;
+}
+
+static Node *rval(Simp *s, Node *n, Node *dst)
+{
+ Node *r; /* expression result */
+ Node *t, *u, *v; /* temporary nodes */
+ Node **args;
+ size_t i;
+ const Op fusedmap[Numops] = {
+ [Oaddeq] = Oadd,
+ [Osubeq] = Osub,
+ [Omuleq] = Omul,
+ [Odiveq] = Odiv,
+ [Omodeq] = Omod,
+ [Oboreq] = Obor,
+ [Obandeq] = Oband,
+ [Obxoreq] = Obxor,
+ [Obsleq] = Obsl,
+ [Obsreq] = Obsr,
+ };
+ const Op cmpmap[Numops][3] = {
+ [Oeq] = {Oeq, Ofeq, Oueq},
+ [One] = {One, Ofne, Oune},
+ [Ogt] = {Ogt, Ofgt, Ougt},
+ [Oge] = {Oge, Ofge, Ouge},
+ [Olt] = {Olt, Oflt, Oult},
+ [Ole] = {Ole, Ofle, Oule}
+ };
+
+ r = NULL;
+ args = n->expr.args;
+ switch (exprop(n)) {
+ case Olor: case Oland:
+ r = simplazy(s, n);
+ break;
+ case Osize:
+ r = mkintlit(n->line, size(args[0]));
+ r->expr.type = exprtype(n);
+ break;
+ case Oslice:
+ r = simpslice(s, n, dst);
+ break;
+ case Oidx:
+ t = idxaddr(s, n->expr.args[0], n->expr.args[1]);
+ r = load(t);
+ break;
+ /* array.len slice.len are magic 'virtual' members.
+ * they need to be special cased. */
+ case Omemb:
+ if (exprtype(args[0])->type == Tyslice || exprtype(args[0])->type == Tyarray) {
+ r = seqlen(s, args[0], exprtype(n));
+ } else {
+ t = membaddr(s, n);
+ r = load(t);
+ }
+ break;
+ case Oucon:
+ r = simpucon(s, n, dst);
+ break;
+ case Ouget:
+ r = simpuget(s, n, dst);
+ break;
+ case Otup:
+ r = simptup(s, n, dst);
+ break;
+ case Oarr:
+ if (!dst)
+ dst = temp(s, n);
+ t = addr(s, dst, exprtype(dst));
+ for (i = 0; i < n->expr.nargs; i++)
+ assignat(s, t, size(n->expr.args[i])*i, n->expr.args[i]);
+ r = dst;
+ break;
+ case Ostruct:
+ if (!dst)
+ dst = temp(s, n);
+ t = addr(s, dst, exprtype(dst));
+ for (i = 0; i < n->expr.nargs; i++)
+ assignat(s, t, offset(n, n->expr.args[i]->expr.idx), n->expr.args[i]);
+ r = dst;
+ break;
+ case Ocast:
+ r = simpcast(s, args[0], exprtype(n));
+ break;
+
+ /* fused ops:
+ * foo ?= blah
+ * =>
+ * foo = foo ? blah*/
+ case Oaddeq: case Osubeq: case Omuleq: case Odiveq: case Omodeq:
+ case Oboreq: case Obandeq: case Obxoreq: case Obsleq: case Obsreq:
+ assert(fusedmap[exprop(n)] != Obad);
+ u = rval(s, args[0], NULL);
+ v = rval(s, args[1], NULL);
+ v = mkexpr(n->line, fusedmap[exprop(n)], u, v, NULL);
+ v->expr.type = u->expr.type;
+ r = set(u, v);
+ break;
+
+ /* ++expr(x)
+ * => args[0] = args[0] + 1
+ * expr(x) */
+ case Opreinc:
+ v = assign(s, args[0], addk(args[0], 1));
+ append(s, v);
+ r = rval(s, args[0], NULL);
+ break;
+ case Opredec:
+ v = assign(s, args[0], subk(args[0], 1));
+ append(s, v);
+ r = rval(s, args[0], NULL);
+ break;
+
+ /* expr(x++)
+ * => expr
+ * x = x + 1
+ */
+ case Opostinc:
+ r = lval(s, args[0]);
+ t = assign(s, r, addk(r, 1));
+ lappend(&s->incqueue, &s->nqueue, t);
+ break;
+ case Opostdec:
+ r = lval(s, args[0]);
+ t = assign(s, r, subk(r, 1));
+ lappend(&s->incqueue, &s->nqueue, t);
+ break;
+ case Olit:
+ switch (args[0]->lit.littype) {
+ case Lchr: case Lbool: case Llbl:
+ r = n;
+ break;
+ case Lint:
+ /* we can only have up to 4 byte immediates, but they
+ * can be moved into 64 bit regs */
+ if (args[0]->lit.intval < 0xffffffffULL)
+ r = n;
+ else
+ r = simpblob(s, n, &s->blobs, &s->nblobs);
+ break;
+ case Lstr: case Lflt:
+ r = simpblob(s, n, &s->blobs, &s->nblobs);
+ break;
+ case Lfunc:
+ r = simpblob(s, n, &file->file.stmts, &file->file.nstmts);
+ break;
+ }
+ break;
+ case Ovar:
+ r = n;
+ break;
+ case Oret:
+ if (s->isbigret) {
+ t = rval(s, args[0], NULL);
+ t = addr(s, t, exprtype(args[0]));
+ u = disp(n->line, size(args[0]));
+ v = mkexpr(n->line, Oblit, s->ret, t, u, NULL);
+ append(s, v);
+ } else if (n->expr.nargs && n->expr.args[0]) {
+ t = s->ret;
+ t = set(t, rval(s, args[0], NULL));
+ append(s, t);
+ }
+ /* drain the increment queue before we return */
+ for (i = 0; i < s->nqueue; i++)
+ append(s, s->incqueue[i]);
+ lfree(&s->incqueue, &s->nqueue);
+ jmp(s, s->endlbl);
+ break;
+ case Oasn:
+ r = assign(s, args[0], args[1]);
+ break;
+ case Ocall:
+ if (exprtype(n)->type != Tyvoid && stacktype(exprtype(n))) {
+ r = temp(s, n);
+ linsert(&n->expr.args, &n->expr.nargs, 1, addr(s, r, exprtype(n)));
+ for (i = 0; i < n->expr.nargs; i++)
+ n->expr.args[i] = rval(s, n->expr.args[i], NULL);
+ append(s, n);
+ } else {
+ r = visit(s, n);
+ }
+ break;
+ case Oaddr:
+ t = lval(s, args[0]);
+ if (exprop(t) == Ovar) /* Ovar is the only one that doesn't return Oderef(Oaddr(...)) */
+ r = addr(s, t, exprtype(t));
+ else
+ r = t->expr.args[0];
+ break;
+ case Oneg:
+ if (istyfloat(exprtype(n))) {
+ t =mkfloat(n->line, -1.0);
+ u = mkexpr(n->line, Olit, t, NULL);
+ t->lit.type = n->expr.type;
+ u->expr.type = n->expr.type;
+ v = simpblob(s, u, &s->blobs, &s->nblobs);
+ r = mkexpr(n->line, Ofmul, v, rval(s, args[0], NULL), NULL);
+ r->expr.type = n->expr.type;
+ } else {
+ r = visit(s, n);
+ }
+ break;
+ case Obreak:
+ if (s->nloopexit == 0)
+ fatal(n->line, "trying to break when not in loop");
+ jmp(s, s->loopexit[s->nloopexit - 1]);
+ break;
+ case Ocontinue:
+ if (s->nloopstep == 0)
+ fatal(n->line, "trying to continue when not in loop");
+ jmp(s, s->loopstep[s->nloopstep - 1]);
+ break;
+ case Oeq: case One: case Ogt: case Oge: case Olt: case Ole:
+ if (istyfloat(tybase(exprtype(args[0]))))
+ i = 1;
+ else if (istysigned(tybase(exprtype(args[0]))))
+ i = 0;
+ else
+ i = 2;
+ n->expr.op = cmpmap[n->expr.op][i];
+ r = visit(s, n);
+ break;
+ default:
+ if (istyfloat(exprtype(n))) {
+ switch (exprop(n)) {
+ case Oadd: n->expr.op = Ofadd; break;
+ case Osub: n->expr.op = Ofsub; break;
+ case Omul: n->expr.op = Ofmul; break;
+ case Odiv: n->expr.op = Ofdiv; break;
+ default: break;
+ }
+ }
+ r = visit(s, n);
+ break;
+ case Obad:
+ die("Bad operator");
+ break;
+ }
+ return r;
+}
+
+static void declarearg(Simp *s, Node *n)
+{
+ assert(n->type == Ndecl || (n->type == Nexpr && exprop(n) == Ovar));
+ s->argsz = align(s->argsz, min(size(n), Ptrsz));
+ htput(s->stkoff, n, itop(-(s->argsz + 2*Ptrsz)));
+ if (debugopt['i']) {
+ dump(n, stdout);
+ printf("declared at %zd\n", -(s->argsz + 2*Ptrsz));
+ }
+ s->argsz += size(n);
+}
+
+static int islbl(Node *n)
+{
+ Node *l;
+ if (exprop(n) != Olit)
+ return 0;
+ l = n->expr.args[0];
+ return l->type == Nlit && l->lit.littype == Llbl;
+}
+
+static Node *simp(Simp *s, Node *n)
+{
+ Node *r, *t, *u;
+ size_t i;
+
+ if (!n)
+ return NULL;
+ r = NULL;
+ switch (n->type) {
+ case Nblock: simpblk(s, n); break;
+ case Nifstmt: simpif(s, n, NULL); break;
+ case Nloopstmt: simploop(s, n); break;
+ case Niterstmt: simpiter(s, n); break;
+ case Nmatchstmt: simpmatch(s, n); break;
+ case Nexpr:
+ if (islbl(n))
+ append(s, n);
+ else
+ r = rval(s, n, NULL);
+ if (r)
+ append(s, r);
+ /* drain the increment queue for this expr */
+ for (i = 0; i < s->nqueue; i++)
+ append(s, s->incqueue[i]);
+ lfree(&s->incqueue, &s->nqueue);
+ break;
+
+ case Ndecl:
+ declarelocal(s, n);
+ if (!n->decl.init)
+ break;
+ t = mkexpr(n->line, Ovar, n->decl.name, NULL);
+ u = mkexpr(n->line, Oasn, t, n->decl.init, NULL);
+ u->expr.type = n->decl.type;
+ t->expr.type = n->decl.type;
+ t->expr.did = n->decl.did;
+ simp(s, u);
+ break;
+ default:
+ die("Bad node passsed to simp()");
+ break;
+ }
+ return r;
+}
+
+/*
+ * Turns a deeply nested function body into a flatter
+ * and simpler representation, which maps easily and
+ * directly to assembly instructions.
+ */
+static void flatten(Simp *s, Node *f)
+{
+ Node *dcl;
+ Type *ty;
+ size_t i;
+
+ assert(f->type == Nfunc);
+ s->nstmts = 0;
+ s->stmts = NULL;
+ s->endlbl = genlbl();
+ s->ret = NULL;
+
+ /* make a temp for the return type */
+ ty = f->func.type->sub[0];
+ if (stacktype(ty)) {
+ s->isbigret = 1;
+ s->ret = gentemp(s, f, mktyptr(f->line, ty), &dcl);
+ declarearg(s, dcl);
+ } else if (ty->type != Tyvoid) {
+ s->isbigret = 0;
+ s->ret = gentemp(s, f, ty, &dcl);
+ }
+
+ for (i = 0; i < f->func.nargs; i++) {
+ declarearg(s, f->func.args[i]);
+ }
+ simp(s, f->func.body);
+
+ append(s, s->endlbl);
+}
+
+static Func *simpfn(Simp *s, char *name, Node *n, Vis vis)
+{
+ size_t i;
+ Func *fn;
+ Cfg *cfg;
+
+ if(debugopt['i'] || debugopt['F'] || debugopt['f'])
+ printf("\n\nfunction %s\n", name);
+
+ /* set up the simp context */
+ /* unwrap to the function body */
+ n = n->expr.args[0];
+ n = n->lit.fnval;
+ pushstab(n->func.scope);
+ flatten(s, n);
+ popstab();
+
+ if (debugopt['f'] || debugopt['F'])
+ for (i = 0; i < s->nstmts; i++)
+ dump(s->stmts[i], stdout);
+ for (i = 0; i < s->nstmts; i++) {
+ if (s->stmts[i]->type != Nexpr)
+ continue;
+ if (debugopt['f']) {
+ printf("FOLD FROM ----------\n");
+ dump(s->stmts[i], stdout);
+ }
+ s->stmts[i] = fold(s->stmts[i], 0);
+ if (debugopt['f']) {
+ printf("TO ------------\n");
+ dump(s->stmts[i], stdout);
+ printf("DONE ----------------\n");
+ }
+ }
+
+ cfg = mkcfg(s->stmts, s->nstmts);
+ if (debugopt['t'] || debugopt['s'])
+ dumpcfg(cfg, stdout);
+
+ fn = zalloc(sizeof(Func));
+ fn->name = strdup(name);
+ if (vis != Visintern)
+ fn->isexport = 1;
+ fn->stksz = align(s->stksz, 8);
+ fn->stkoff = s->stkoff;
+ fn->ret = s->ret;
+ fn->cfg = cfg;
+ return fn;
+}
+
+static void fillglobls(Stab *st, Htab *globls)
+{
+ void **k;
+ size_t i, nk;
+ Stab *stab;
+ Node *s;
+
+ k = htkeys(st->dcl, &nk);
+ for (i = 0; i < nk; i++) {
+ s = htget(st->dcl, k[i]);
+ htput(globls, s, asmname(s->decl.name));
+ }
+ free(k);
+
+ k = htkeys(st->ns, &nk);
+ for (i = 0; i < nk; i++) {
+ stab = htget(st->ns, k[i]);
+ fillglobls(stab, globls);
+ }
+ free(k);
+}
+
+static void extractsub(Simp *s, Node ***blobs, size_t *nblobs, Node *e)
+{
+ size_t i;
+
+ switch (exprop(e)) {
+ case Oslice:
+ if (exprop(e->expr.args[0]) == Oarr)
+ e->expr.args[0] = simpblob(s, e->expr.args[0], blobs, nblobs);
+ break;
+ case Oarr:
+ case Ostruct:
+ for (i = 0; i < e->expr.nargs; i++)
+ extractsub(s, blobs, nblobs, e->expr.args[i]);
+ break;
+ default:
+ break;
+ }
+}
+
+static void simpconstinit(Simp *s, Node *dcl)
+{
+ Node *e;
+
+ dcl->decl.init = fold(dcl->decl.init, 1);;
+ e = dcl->decl.init;
+ if (e && exprop(e) == Olit) {
+ if (e->expr.args[0]->lit.littype == Lfunc)
+ simpblob(s, e, &file->file.stmts, &file->file.nstmts);
+ else
+ lappend(&s->blobs, &s->nblobs, dcl);
+ } else if (dcl->decl.isconst) {
+ switch (exprop(e)) {
+ case Oarr:
+ case Ostruct:
+ case Oslice:
+ extractsub(s, &s->blobs, &s->nblobs, e);
+ lappend(&s->blobs, &s->nblobs, dcl);
+ break;
+ default:
+ break;
+ }
+ } else if (!dcl->decl.isconst && !e) {
+ lappend(&s->blobs, &s->nblobs, dcl);
+ } else {
+ die("Non-constant initializer for %s\n", declname(dcl));
+ }
+}
+
+static void simpglobl(Node *dcl, Htab *globls, Func ***fn, size_t *nfn, Node ***blob, size_t *nblob)
+{
+ Simp s = {0,};
+ char *name;
+ Func *f;
+
+ name = asmname(dcl->decl.name);
+ s.stkoff = mkht(varhash, vareq);
+ s.globls = globls;
+ s.blobs = *blob;
+ s.nblobs = *nblob;
+
+ if (dcl->decl.isextern || dcl->decl.isgeneric)
+ return;
+ if (isconstfn(dcl)) {
+ f = simpfn(&s, name, dcl->decl.init, dcl->decl.vis);
+ lappend(fn, nfn, f);
+ } else {
+ simpconstinit(&s, dcl);
+ }
+ *blob = s.blobs;
+ *nblob = s.nblobs;
+ free(name);
+}
+
+void gen(Node *file, char *out)
+{
+ Htab *globls, *strtab;
+ Node *n, **blob;
+ Func **fn;
+ size_t nfn, nblob;
+ size_t i;
+ FILE *fd;
+
+ /* declare useful constants */
+ tyintptr = mktype(-1, Tyuint64);
+ tyword = mktype(-1, Tyuint);
+ tyvoid = mktype(-1, Tyvoid);
+
+ fn = NULL;
+ nfn = 0;
+ blob = NULL;
+ nblob = 0;
+ globls = mkht(varhash, vareq);
+
+ /* We need to define all global variables before use */
+ fillglobls(file->file.globls, globls);
+
+ pushstab(file->file.globls);
+ for (i = 0; i < file->file.nstmts; i++) {
+ n = file->file.stmts[i];
+ switch (n->type) {
+ case Nuse: /* nothing to do */
+ case Nimpl:
+ break;
+ case Ndecl:
+ simpglobl(n, globls, &fn, &nfn, &blob, &nblob);
+ break;
+ default:
+ die("Bad node %s in toplevel", nodestr(n->type));
+ break;
+ }
+ }
+ popstab();
+
+ fd = fopen(out, "w");
+ if (!fd)
+ die("Couldn't open fd %s", out);
+
+ strtab = mkht(strhash, streq);
+ fprintf(fd, ".data\n");
+ for (i = 0; i < nblob; i++)
+ genblob(fd, blob[i], globls, strtab);
+ fprintf(fd, ".text\n");
+ for (i = 0; i < nfn; i++)
+ genasm(fd, fn[i], globls, strtab);
+ genstrings(fd, strtab);
+ fclose(fd);
+}
--- /dev/null
+++ b/LICENSE
@@ -1,0 +1,19 @@
+Copyright (c) 2013 Ori Bernstein <[email protected]>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
--- /dev/null
+++ b/Makefile
@@ -1,0 +1,14 @@
+SUB = parse \
+ mi \
+ 6 \
+ muse \
+ myrbuild \
+ myrtypes \
+ libstd \
+ doc
+
+include mk/c.mk
+include config.mk
+
+check: all
+ make -C test check
--- /dev/null
+++ b/TODO
@@ -1,0 +1,8 @@
+TODO:
+ - Closures [Tests: closure]
+ - Not-boneheaded asm [Tests: ]
+ - Optimized asm [Tests: ]
+ - Use-def analysis [Tests: usedef]
+ - Deferred code [Tests: ]
+ - Module-mains [Tests: ]
+ - User defined traits [Tests: ]
--- /dev/null
+++ b/bench/intsort.myr
@@ -1,0 +1,18 @@
+use std
+
+const main = {
+ var a
+ var i
+ var rng
+ var t0, t1
+
+ rng = std.mksrng(123)
+ a = std.slalloc(1000000)
+ for i = 0; i < a.len; i++
+ a[i] = std.rand32(rng)
+ ;;
+ t0 = std.now()
+ std.sort(a, std.numcmp)
+ t1 = std.now()
+ std.put("time = %l\n", t1 - t0)
+}
--- /dev/null
+++ b/configure
@@ -1,0 +1,64 @@
+#!/bin/sh
+
+for i in `seq 300`; do
+ echo "Lots of output to emulate automake... ok"
+ echo "Testing for things you'll never use... fail"
+ echo "Satisfying the fortran77 lobby... ok"
+ echo "Burning CPU time checking for the bloody obvious... ok"
+done
+echo "Automake emulated successfully"
+
+INST_ROOT='/usr/local'
+prefix="/usr/local"
+
+for arg in $*; do
+ shift 1
+ case $arg in
+ "--prefix" | "-p")
+ prefix=shift $*
+ ;;
+ --prefix=*)
+ prefix=`echo $arg | sed 's/^--prefix=//g'`
+ ;;
+ "--help" | "-h")
+ echo "Usage:"
+ echo " --prefix | -p: The prefix to install to"
+ break;
+ ;;
+ *) echo "Unrecognized argument $arg";;
+ esac
+done
+
+OS=`uname`
+
+echo export INST_ROOT=$prefix > config.mk
+
+echo '#define Instroot "'$prefix'"' > config.h
+case $OS in
+ *Linux*)
+ echo '#define Asmcmd "as -g -o %s %s"' >> config.h
+ echo '#define Fprefix ""' >> config.h
+ echo 'export SYS=linux' >> config.mk
+ ;;
+ *Darwin*)
+ echo '#define Asmcmd "as -g -o %s %s"' >> config.h
+ echo '#define Fprefix "_"' >> config.h
+ echo 'export SYS=osx' >> config.mk
+ ;;
+ *)
+ echo ''
+ echo ''
+ echo '********************************'
+ echo 'Unknown architecture.'
+ echo 'Cannot build'
+ echo '********************************'
+ rm -f config.h config.mk
+ exit 1
+ ;;
+esac
+
+cat << EOF
+ Building with:
+ prefix=$prefix
+EOF
+
--- /dev/null
+++ b/doc/Makefile
@@ -1,0 +1,21 @@
+MAN=mc.1 \
+ muse.1 \
+ myrbuild.1 \
+
+include ../config.mk
+
+all:
+
+install:
+ @echo install -m 644 $(MAN) $(abspath $(DESTDIR)/$(INST_ROOT)/share/man/man1); \
+ mkdir -p $(abspath $(DESTDIR)/$(INST_ROOT)/share/man/man1); \
+ install -m 644 $(MAN) $(abspath $(DESTDIR)/$(INST_ROOT)/share/man/man1); \
+
+uninstall: $(MAN)
+ @for i in $^; do \
+ echo rm -f $(abspath $(DESTDIR)/$(INST_ROOT)/share/man/man1/$$i); \
+ rm -f $(abspath $(DESTDIR)/$(INST_ROOT)/share/man/man1/$$i); \
+ done
+
+clean:
+
--- /dev/null
+++ b/doc/compiler.txt
@@ -1,0 +1,336 @@
+ Structure of the Myrddin Compiler
+ Aug 2012
+ Ori Bernstein
+
+TABLE OF CONTENTS:
+
+ 1. OVERVIEW
+ 1.1. Tree Structure
+ 2. PARSING
+ 2.1. Lexing
+ 2.2. AST Creation
+ 2.3. Type checking
+ 2.4. Generic Specialization
+ 2.5. Serialization
+ 2.6. Usefiles
+ 3. FLATTENING
+ 3.1. Control Flow
+ 3.2. Complex Expressions
+ 4. OPTIMIZATION
+ 4.1. Constant Folding
+ 5. CODE GENERATION
+ 5.1. Instruction Selection
+ 5.2. Register Allocation
+ 6. TUTORIAL: ADDING A STATEMENT
+ 6.1. Stubbing in the node types
+ 6.2. Parsing
+ 6.3. Flattening
+ 6.4. Optimization
+ 6.5. Instruction Selection
+
+1. OVERVIEW:
+
+ The Myrddin compiler suite consists of a set of binaries, written in C,
+ which translate Myrddin source code to the assembly most appropriate for
+ the target platform, and subsequently invoke the native assembler on it.
+ The linker is not invoked by the compiler, and the final output is an
+ object file for the target platform.
+
+ The compilers are named with a single character for the target platform,
+ with a single character for the language being compiled. A table of the
+ compilers and their names is below:
+
+ Compiler Platform
+ -------------------------
+ 6m x86-64
+
+
+ The compilation is divided into a small number of phases. The first phase
+ is parsing, where the source code is first tokenized, the abstract syntax
+ tree (AST) is generated, and semantically checked. The second phase is the
+ machine-dependent tree flattening. In this phase, the tree is decomposed
+ function by function into simple operations that are relatively close to
+ the machine. Sizes are fixed, and all loops, if statements, etc. are
+ replaced with gotos. The next phase is a machine-independent optimizer,
+ which currenty does nothing other than simply folding trees. In the final
+ phase, the instructions are selected and the registers are allocated.
+
+ So, to recap, the phases are as follows:
+
+ parse Tokenize, parse, and analyze the source
+ flatten Rewrite the complex nodes into simpe ones
+ opt Optimize the flattened source trees
+ gen Generate the assembly code
+
+ 1.1. Tree Structure:
+
+ File nodes (n->type == Nfile) represent the files being compiled. The current
+ node is held in a global variable called, unsurprisingly, 'file'. The
+ global symbol table, export table, uses, and other compilation-specific
+ information is stored in this node. This implies that the compiler can
+ only process one file at a time.
+
+ Name nodes (n->type == Nname) are simply names, possibly with a namespace
+ attached. They are left as leaf nodes in the tree, specifying variable
+ names, union tags, and just about anything else with a name.
+
+ Use nodes (n->type == Nuse) simply tell the compiler that a usefile by the
+ name stored in this node will be loaded.
+
+ Expression nodes (n->type == Nexpr) represent expressions. They consist of
+ an operator, a type, a few flags, possibly a declaration ID, and a list of
+ arguments.
+
+ Operators are defined in parse/ops.def, and start with an 'O' by
+ convention; eg: Oadd, Osub, etc.
+
+ The declaration id (n->expr.did) is only valid on expressions representing
+ a single variable (n->expr.op == Ovar). The DID is a unique identifier
+ representing the declaration node that the variable refers to. This is used
+ for a variety of things, from fast comparisons to allowing us to put the
+ node into a bit set easily.
+
+ Literal nodes (n->type == Nlit) hold a literal value. The type held is
+ stored in littype, which are defined in parse/lits.def.
+
+ The various statement nodes (Nloopstmt, Nifstmt, Nmatchstmt, Nblock,
+ Nlbl) are all statements that may appear within a block node (Nblock).
+
+ Declaration nodes declare a name in a symbol table. TODO: MORE DETAIL.
+
+ Uelt nodes declare a union element. TODO: MORE DETAIL.
+
+ Func nodes declare a function. TODO: MORE DETAIL.
+
+
+
+2. PARSING:
+
+ This phase takes in a source file, and outputs a tree that is guaranteed
+ to be valid. The tree nodes are defined in parse/parse.h in struct Node,
+ and have one of the types defined in parse/nodetypes.def. Node types
+ start with 'N' by convention; eg: Nfile, Nifstmt, etc.
+
+ 2.1. Lexing:
+
+ Lexing occurs in parse/tok.c. Because we want to use this lexer from
+ within yacc, the entry point to this code is in 'yylex()'. As required
+ by yacc, 'yylex()' returns an integer defining the token type, and
+ sets the 'tok' member of yylval to the token that was taken from the
+ input stream. In addition, to allow for better error messages, the
+ global variable 'curtok' is set to the value of 'yylval.tok'. This
+ allows yyerror to print the last token that was seen.
+
+ The tokens that are allowable are generated by Yacc from the '%token'
+ definitions in parse/gram.y, and are placed into the file
+ 'parse/gram.h'. The lexer and parser code is the only code that
+ depends on these token constants.
+
+ The lexer is initalized through 'tokinit(char *file)'. This function
+ will open the file passed in, read all the data from it in one go
+ and set up the internal data for the tokenizer. The tokenizing is then
+ done while the whole file is in memory, which means that this code
+ will work poorly on files that are larger than the address space
+ available to the compiler. If this is a problem, you deserve all the
+ pain that is caused.
+
+ The file data is stored in the three global variables 'fidx', 'fbuf',
+ and 'fbufsz'. The actual tokenization happens through 'toknext()' and
+ its callees, which operate on these data structures character by
+ character, matching the values read, and shoving them into the 'Tok'
+ data structure.
+
+ 2.2. AST Creation:
+
+ The parser used is a traditional Yacc-based parser. It is generated
+ from the source in parse/gram.y. The starting production is 'file',
+ which fills in a global 'file' tree node. This 'file' tree node must
+ be initialized before yyparse() is called.
+
+
+ 2.3. Type Checking:
+
+ Type checking is done through unification of types. It's implemented
+ in parse/infer.c. It proceeds through a simple unification algorithm,
+ which is documented in lang.txt. As a result, only the internal
+ details of this algorithm will be discussed here.
+
+ The first step done is loading and resolving use files. This is
+ deferred to the type checking phase for two reasons. First, we
+ do not want to force tools to have all dependencies compiled if they
+ use this parser, even though type full type checking is impossible
+ until all usefiles are loaded. And second, this is when the
+ information is actually needed.
+
+ Next, the types declared in the package section are merged with the
+ exported types, allowing us to start off with our type information as
+ complete as possible, and making sure that the types of globals
+ actually match up with the exported types.
+
+ The next step is the actual type inference. We do a bottom-up walk of
+ the tree, unifying types as we go. There are subtleties with the
+ member operator, however. Because the '.' operator is used for both
+ member lookups and namespace lookups, before we descend into a node
+ that has operator Omemb, we need to check if it's a namespaced name,
+ or an actual member reference. If it is a namespaced name, we replace
+ the expression with an Ovar expression. This check happens in the
+ 'checkns()' function. Second, because we need to know the LHS of a
+ member expression before we can check if the RHS is valid, and we
+ are not guaranteed to know this at the first time that we see it, the
+ expression is assumed to be valid, and this asumption is verified in
+ a post-processing pass. Casts are validated in a deferred manner
+ similarly.
+
+ Generic variables are added to a list of generic callsites to
+ specialize when they are seen in as a leaf of an Ovar node.
+
+ The type inference, to this point, has only built up a mapping
+ of types. So, for example, if we were to have the inferred types
+ for the following set of statements:
+
+ var a
+ var b
+ var c
+ a = b
+ c = b + 1
+
+ We would have the mappings:
+
+ $t0 -> $t1
+ $t1 -> $t2
+ $t2 -> int
+
+ So, in the 'typesub()' function, we iterate over the entire tree,
+ replacing every instance of a non-concrete type with the final
+ mapped type. If a type does not map to a fully concrete type,
+ this is where we flag an error.
+
+ FIXME: DESCRIBE HOW YOU FIXED GENERICS ONCE YOU FIX GENERICS.
+
+ 2.4. Generic Specialization:
+
+ After type inference (well, technially, as the final step of it),
+ we walk through the list of callsites that need instantiations
+ of generics, and create a specialized generic instance for each of
+ them. This specialization is done, unsurprisingly, in specialize.c,
+ by the simple algorithm of cloning the entire tree that needs to
+ be specialized, and walking over all nodes substituting the types
+ that are replacing the type parameters.
+
+ 2.5. Serialization:
+
+ Trees of all sorts can be serialized and deserialized from files,
+ as long as they are fully typed. Trees containing type variables (ie,
+ uninferred types) cannot be serialized, as type variables cannot be
+ deserialized meaningfully.
+
+ The format for this is only documented in the source, and is a
+ straighforward dump of the trees to memory. It is constantly shifting,
+ and will not reliably work between compiler versions.
+
+ 2.6. Usefiles:
+
+ Usefiles are more or less files that consist of a single character tag
+ that tells us what type of tree to deserialize. Because serialized
+ trees are compiler version dependent, so are usefiles.
+
+3. FLATTENING:
+
+ This phase is invoked repeatedly on each top-level declaration that we
+ want to generate code for. There is a good chance that this flattening
+ phase should be made machine-independent, and passed as a parameter
+ a machine description describing known integer and pointer sizes, among
+ other machine attributes. However, for now, it is machine-dependent,
+ and lives in 6/simp.c.
+
+ The goal of flattening a tree is to take semantically involved constructs
+ such as looping, and simplify things into something that is easy to
+ generate code for, as well as something that is easier to analyze for
+ optimization.
+
+ 3.1. Control Flow:
+
+ All if statements, loops, and other complex constructs are simplified
+ to jumps and conditional jumps. Loops are generally simplified from
+ something that would look like this:
+
+ loop
+ init
+ cond
+ inc
+ body
+
+ To something that would look like this:
+
+ init
+ jmp cond
+ .loop:
+ body
+ inc
+ .cond:
+ cjmp cond .loop .end
+ .end:
+
+ Boolean expressions are simplified to a location to jump to, as
+ described in section 8.4 of the Dragon book[1].
+
+ 3.2. Complex Expressions:
+
+ Complex expressions such as copying types larger than a single machine
+ word, pulling members out of structures, emulating multiplication and
+ division for larger integers sizes, and similar operations are reduced
+ to trees that are expressible in terms of simple machine operations.
+
+ By the end of the simplification pass, the following operators should
+ not be present in the trees:
+
+ Obad Oret Opreinc Opostinc Opredec Opostdec Olor Oland Oaddeq
+ Osubeq Omuleq Odiveq Omodeq Oboreq Obandeq Obxoreq Obsleq
+ Obsreq Omemb Oslice Oidx Osize Numops Oucon Ouget Otup Oarr
+ Oslbase Osllen Ocast
+
+
+4. OPTIMIZATION:
+
+ Currently, there is virtually no optimization done on the trees after
+ flattening. The only optimization that is done is constant folding.
+
+ 4.1. Constant Folding:
+
+ Expressions with constant values are simplified algebraically. For
+ example, the expression 'x*1' is simplified to 'x', '0/n' is
+ simplified to '0', and so on.
+
+
+5. CODE GENERATION:
+
+ 5.1. Instruction Selection:
+
+ Instruction selection is done via a simple handwritten bottom-up pass
+ over the tree. Common patterns such as scaled or offset indexing are
+ recognized by the patterns, but no attempts are made at finding an
+ optimal tiling.
+
+ 5.2. Register Allocation:
+
+ Register allocation is done via the algorithm described in "Iterated
+ Regster Coalescing" by Appel and George. As of the time of this
+ writing, the register allocator does not yet implement overlapping
+ register classes. This will be done as described in "A generalized
+ algorithm for graph-coloring register allocation" by Smith, Ramsey,
+ and Holloway.
+
+6: TUTORIAL: ADDING A STATEMENT:
+
+ 6.1. Stubbing in the node types:
+
+ 6.2. Parsing:
+
+ 6.3. Flattening:
+
+ 6.4. Optimization:
+
+ 6.5. Instruction Selection:
+
+[1] Aho, Sethi, Ullman: Compilers: Principles, Techniques, and Tools, 1988.
+ ISBN 0-201-10088-6
--- /dev/null
+++ b/doc/lang.txt
@@ -1,0 +1,832 @@
+ The Myrddin Programming Language
+ Jul 2012
+ Ori Bernstein
+
+TABLE OF CONTENTS:
+
+ 1. ABOUT
+ 2. LEXICAL CONVENTIONS
+ 3. SYNTAX
+ 3.1. Declarations
+ 3.2. Literal Values
+ 3.3. Control Constructs and Blocks
+ 3.4. Expressions
+ 3.5. Data Types
+ 3.6. Packages and Uses
+ 4. TOOLCHAIN
+ 5. EXAMPLES
+ 6. STYLE GUIDE
+ 7. STANDARD LIBRARY
+ 8. GRAMMAR
+ 9. FUTURE DIRECTIONS
+
+1. ABOUT:
+
+ Myrddin is designed to be a simple, low-level programming
+ language. It is designed to provide the programmer with
+ predictable behavior and a transparent compilation model,
+ while at the same time providing the benefits of strong
+ type checking, generics, type inference, and similar.
+ Myrddin is not a language designed to explore the forefront
+ of type theory or compiler technology. It is not a language
+ that is focused on guaranteeing perfect safety. Its focus
+ is on being a practical, small, fairly well defined, and
+ easy to understand language for work that needs to be close
+ to the hardware.
+
+ Myrddin is a computer language influenced strongly by C
+ and ML, with ideas from Rust, Go, C++, and numerous other
+ sources and resources.
+
+
+2. LEXICAL CONVENTIONS:
+
+ The language is composed of several classes of tokens. There
+ are comments, identifiers, keywords, punctuation, and whitespace.
+
+ Comments begin with "/*" and end with "*/". They may nest.
+
+ /* this is a comment /* with another inside */ */
+
+ Identifiers begin with any alphabetic character or underscore,
+ and continue with any number of alphanumeric characters or
+ underscores. Currently the compiler places a limit of 1024
+ bytes on the length of the identifier.
+
+ some_id_234__
+
+ Keywords are a special class of identifier that is reserved
+ by the language and given a special meaning. The set of
+ keywords in Myrddin are as follows:
+
+ castto match
+ const pkg
+ default protect
+ elif sizeof
+ else struct
+ export trait
+ extern true
+ false type
+ for union
+ generic use
+ goto var
+ if while
+
+
+ At the current stage of development, not all of these keywords are
+ implemented within the language.[1]
+
+ Literals are a direct representation of a data object within the source of
+ the program. There are several literals implemented within the language.
+ These are fully described in section 3.2 of this manual.
+
+ In the compiler, single semicolons (';') and newline (\x10)
+ characters are treated identically, and are therefore interchangable.
+ They will both be referred to "endline"s thoughout this manual.
+
+
+3. SYNTAX OVERVIEW:
+
+ 3.1. Declarations:
+
+ A declaration consists of a declaration class (i.e., one
+ of 'const', 'var', or 'generic'), followed by a declaration
+ name, optionally followed by a type and assignment. One thing
+ you may note is that unlike most other languages, there is no
+ special function declaration syntax. Instead, a function is
+ declared like any other value: by assigning its name to a
+ constant or variable.
+
+ const: Declares a constant value, which may not be
+ modified at run time. Constants must have
+ initializers defined.
+ var: Declares a variable value. This value may be
+ assigned to, copied from, and modified.
+ generic: Declares a specializable value. This value
+ has the same restricitions as a const, but
+ taking its address is not defined. The type
+ parameters for a generic must be explicitly
+ named in the declaration in order for their
+ substitution to be allowed.
+
+ In addition, there is one modifier allowed on declarations:
+ 'extern'. Extern declarations are used to declare symbols from
+ another module which cannot be provided via the 'use' mechanism.
+ Typical uses would be to expose a function written in assembly. They
+ can also be used as a workaround for external dependencies.
+
+ Examples:
+
+ Declare a constant with a value 123. The type is not defined,
+ and will be inferred:
+
+ const x = 123
+
+ Declare a variable with no value and no type defined. The
+ value can be assigned later (and must be assigned before use),
+ and the type will be inferred.
+
+ var y
+
+ Declare a generic with type '@a', and assigns it the value
+ 'blah'. Every place that 'z' is used, it will be specialized,
+ and the type parameter '@a' will be substituted.
+
+ generic z : @a = blah
+
+ Declare a function f with and without type inference. Both
+ forms are equivalent. 'f' takes two parameters, both of type
+ int, and returns their sum as an int
+
+ const f = {a, b
+ var c : int = 42
+ -> a + b + c
+ }
+
+ const f : (a : int, b : int -> int) = {a : int, b : int -> int
+ var c : int = 42
+ -> a + b + c
+ }
+
+ 3.2. Literal Values
+
+ Integers literals are a sequence of digits, beginning with a
+ digit and possibly separated by underscores. They are of a
+ generic type, and can be used where any numeric type is
+ expected. They may be prefixed with "0x" to indicate that the
+ following number is a hexadecimal value, or 0b to indicate a
+ binary value. Decimal values are not prefixed, and octal values
+ are not supported.
+
+ eg: 0x123_fff, 0b1111, 1234
+
+ Floating-point literals are also a sequence of digits beginning with
+ a digit and possibly separated by underscores. They are also of a
+ generic type, and may be used whenever a floating-point type is
+ expected. Floating point literals are always in decimal, and
+ as of this writing, exponential notation is not supported[2]
+
+ eg: 123.456
+
+ String literals represent a compact method of representing a byte
+ array. Any byte values are allowed in a string literal, and will be
+ spit out again by the compiler unmodified, with the exception of
+ escape sequences.
+
+ There are a number of escape sequences supported for both character
+ and string literals:
+ \n newline
+ \r carriage return
+ \t tab
+ \b backspace
+ \" double quote
+ \' single quote
+ \v vertical tab
+ \\ single slash
+ \0 nul character
+ \xDD single byte value, where DD are two hex digits.
+
+ String literals begin with a ", and continue to the next
+ unescaped ".
+
+ eg: "foo\"bar"
+
+ Character literals represent a single codepoint in the character
+ set. A character starts with a single quote, contains a single
+ codepoint worth of text, encoded either as an escape sequence
+ or in the input character set for the compiler (generally UTF8).
+ They share the same set of escape sequences as string literals.
+
+ eg: 'א', '\n', '\u1234'[3]
+
+ Boolean literals are either the keyword "true" or the keyword
+ "false".
+
+ eg: true, false
+
+ Funciton literals describe a function. They begin with a '{',
+ followed by a newline-terminated argument list, followed by a
+ body and closing '}'. They will be described in more detail
+ later in this manual.
+
+ eg: {a : int, b
+ -> a + b
+ }
+
+ Sequence literals describe either an array or a structure
+ literal. They begin with a '[', followed by an initializer
+ sequence and closing ']'. For array literals, the initializer
+ sequence is either an indexed initializer sequence[4], or an
+ unindexed initializer sequence. For struct literals, the
+ initializer sequence is always a named initializer sequence.
+
+ An unindexed initializer sequence is simply a comma separated
+ list of values. An indexed initializer sequence contains a
+ '#number=value' comma separated sequence, which indicates the
+ index of the array into which the value is inserted. A named
+ initializer sequence contains a comma separated list of
+ '.name=value' pairs.
+
+ eg: [1,2,3], [#2=3, #1=2, #0=1], [.a = 42, .b="str"]
+
+ A tuple literal is a parentheses separated list of values.
+ A single element tuple contains a trailing comma.
+
+ eg: (1,), (1,'b',"three")
+
+ Finally, while strictly not a literal, it's not a control
+ flow construct either. Labels are identifiers preceded by
+ colons.
+
+ eg: :my_label
+
+ They can be used as targets for gotos, as follows:
+
+ goto my_label
+
+ the ':' is not part of the label name.
+
+ 3.3. Control Constructs and Blocks:
+
+ if for
+ while match
+ goto
+
+ The control statements in Myrddin are similar to those in many other
+ popular languages, and with the exception of 'match', there should
+ be no surprises to a user of any of the Algol derived languages.
+ Where a truth value is required, any type with the builtin trait
+ 'tctest' can be used in all of these.
+
+ Blocks are the "carriers of code" in Myrddin programs. They consist
+ of series of expressions, typically ending with a ';;', although the
+ function-level block ends at the function's '}', and in if
+ statemments, an 'elif' may terminate a block. They can contain any
+ number of declarations, expressions, control constructs, and empty
+ lines. Every control statement example below will (and, in fact,
+ must) have a block attached to the control statement.
+
+ If statements branch one way or the other depending on the truth
+ value of their argument. The truth statement is separated from the
+ block body
+
+ if true
+ std.put("The program always get here")
+ elif elephant != mouse
+ std.put("...eh.")
+ else
+ std.put("The program never gets here")
+ ;;
+
+ For statements begin with an initializer, followed by a test
+ condition, followed by an increment action. For statements run the
+ initializer once before the loop is run, the test each on each
+ iteration through the loop before the body, and the increment on
+ each iteration after the body. If the loop is broken out of early
+ (for example, by a goto), the final increment will not be run. The
+ syntax is as follows:
+
+ for init; test; increment
+ blockbody()
+ ;;
+
+ While loops are equivalent to for loops with empty initializers
+ and increments. They run the test on every iteration of the loop,
+ and exit only if it returns false.
+
+ Match statements do pattern matching on values. They take as an
+ argument a value of type 't', and match it against a list of other
+ values of the same type. The patterns matched against can also contain
+ free names, which will be bound to the sub-value matched against. The
+ patterns are checked in order, and the first matching pattern has its
+ body executed, after which no other patterns will be matched. This
+ implies that if you have specific patterns mixed with by more general
+ ones, the specific patterns must come first.
+
+ Match patterns can be one of the following:
+
+ - Union patterns
+
+ These look like union constructors, only they define
+ a value to match against.
+
+ - Literal patterns
+
+ Any literal value can be matched against.
+
+ - Constant patterns
+
+ Any constant value can be matched against.
+
+ More types of pattern to match will be added over time.
+
+ Match statements consist of the keyord 'match', followed by
+ the expression to match against the patterns, followed by a
+ newline. The body of the match statement consists of a list
+ of pattern clauses. A patterned clause is a pattern, followed
+ by a ':', followed by a block body.
+
+ An example of the syntax follows:
+
+ const Val234 = `Val 234 /* set up a constant value */
+ var v = `Val 123 /* set up variable to match */
+ match v
+ /* pattern clauses */
+ `Val 123:
+ std.put("Matched literal union pat\n");;
+ Val234:
+ std.put("Matched const value pat\n")
+ ;;
+ `Val a:
+ std.put("Matched pattern with capture\n")
+ std.put("Captured value: a = %i\n", a)
+ ;;
+ a
+ std.put("A top level bind matches anything.");;
+ `Val 111
+ std.put("Unreachable block.")
+ ;;
+ ;;
+
+
+ 3.4. Expressions:
+
+ Myrddin expressions are relatively similar to expressions in C. The
+ operators are listed below in order of precedence, and a short
+ summary of what they do is listed given. For the sake of clarity,
+ 'x' will stand in for any expression composed entirely of
+ subexpressions with higher precedence than the current current
+ operator. 'e' will stand in for any expression. Unless marked
+ otherwise, expressions are left associative.
+
+ BUG: There are too many precedence levels.
+
+
+ Precedence 14: (*ok, not really operators)
+ (,,,) Tuple Construction
+ (e) Grouping
+ name Bare names
+ literal Values
+
+ Precedence 13:
+ x.name Member lookup
+ x++ Postincrement
+ x-- Postdecrement
+ x[e] Index
+ x[from,to] Slice
+
+ Precedence 12:
+ ++x Preincrement
+ --x Predecrement
+ *x Dereference
+ &x Address
+ !x Logical negation
+ ~x Bitwise negation
+ +x Positive (no operation)
+ -x Negate x
+
+ Precedence 11:
+ x << x Shift left
+ x >> x Shift right
+
+ Precedence 10:
+ x * x Multiply
+ x / x Divide
+ x % x Modulo
+
+ Precedence 9:
+ x + x Add
+ x - x Subtract
+
+ Precedence 8:
+ x & y Bitwise and
+
+ Precedence 7:
+ x | y Bitwise or
+ x ^ y Bitwise xor
+
+ Precedence 6:
+ `Name x Union construction
+
+ Precedence 5:
+ x castto(type) Cast expression
+
+ Precedence 4:
+ x == x Equality
+ x != x Inequality
+ x > x Greater than
+ x >= x Greater than or equal to
+ x < x Less than
+ x <= x Less than or equal to
+
+ Precedence 3:
+ x && x Logical and
+
+ Precedence 2:
+ x || x Logical or
+
+ Precedence 1:
+ x = x Assign Right assoc
+ x += x Fused add/assign Right assoc
+ x -= x Fused sub/assign Right assoc
+ x *= x Fused mul/assign Right assoc
+ x /= x Fused div/assign Right assoc
+ x %= x Fused mod/assign Right assoc
+ x |= x Fused or/assign Right assoc
+ x ^= x Fused xor/assign Right assoc
+ x &= x Fused and/assign Right assoc
+ x <<= x Fused shl/assign Right assoc
+ x >>= x Fused shr/assign Right assoc
+
+ Precedence 0:
+ -> x Return expression
+
+ All expressions on integers act on two's complement values which wrap
+ on overflow. Right shift expressions fill with the sign bit on
+ signed types, and fill with zeros on unsigned types.
+
+ 3.5. Data Types:
+
+ The language defines a number of built in primitive types. These
+ are not keywords, and in fact live in a separate namespace from
+ the variable names. Yes, this does mean that you could, if you want,
+ define a variable named 'int'.
+
+ There are no implicit conversions within the language. All types
+ must be explicitly cast if you want to convert, and the casts must
+ be of compatible types, as will be described later.
+
+ 3.5.1. Primitive types:
+
+ void
+ bool char
+ int8 uint8
+ int16 uint16
+ int32 uint32
+ int64 uint64
+ int uint
+ long ulong
+ float32 float64
+
+ These types are as you would expect. 'void' represents a
+ lack of type, although for the sake of genericity, you can
+ assign between void types, return values of void, and so on.
+ This allows generics to not have to somehow work around void
+ being a toxic type.
+
+ bool is a type that can only hold true and false. It can be
+ assigned, tested for equality, and used in the various boolean
+ operators.
+
+ char is a 32 bit integer type, and is guaranteed to be able
+ to hold exactly one codepoint. It can be assigned integer
+ literals, tested against, compared, and all the other usual
+ numeric types.
+
+ The various [u]intXX types hold, as expected, signed and
+ unsigned integers of the named sizes respectively.
+ Similarly, floats hold floating point types with the
+ indicated precision.
+
+ var x : int declare x as an int
+ var y : float32 declare y as a 32 bit float
+
+
+ 3.5.2. Composite types:
+
+ pointer
+ slice array
+
+ Pointers are, as expected, values that hold the address of
+ the pointed to value. They are declared by appending a '*'
+ to the type. Pointer arithmetic is not allowed. They are
+ declared by appending a '*' to the base type
+
+ Arrays are a group of N values, where N is part of the type.
+ Arrays of different sizes are incompatible. Arrays in
+ Myrddin, unlike many other languages, are passed by value.
+ They are declared by appending a '[SIZE]' to the base type.
+
+ Slices are similar to arrays in many contemporary languages.
+ They are reference types that store the length of their
+ contents. They are declared by appending a '[,]' to the base
+ type.
+
+ foo* type: pointer to foo
+ foo[123] type: array of 123 foo
+ foo[,] type: slice of foo
+
+ 3.5.3. Aggregate types:
+
+ tuple struct
+ union
+
+ Tuples are the traditional product type. They are declared
+ by putting the comma separated list of types within square
+ brackets.
+
+ Structs are aggregations of types with named members. They
+ are declared by putting the word 'struct' before a block of
+ declaration cores (ie, declarations without the storage type
+ specifier).
+
+ Unions are the traditional sum type. They consist of a tag
+ (a keyword prefixed with a '`' (backtick)) indicating their
+ current contents, and a type to hold. They are declared by
+ placing the keyword 'union' before a list of tag-type pairs.
+ They may also omit the type, in which case, the tag is
+ suficient to determine which option was selected.
+
+ [int, int, char] a tuple of 2 ints and a char
+
+ struct a struct containing an int named
+ a : int 'a', and a char named 'b'.
+ b : char
+ ;;
+
+ union a union containing one of
+ `Thing int int or char. The values are not
+ `Other float32 named, but they are tagged.
+ ;;
+
+
+ 3.5.4. Magic types:
+
+ tyvar typaram
+ tyname
+
+ A tyname is a named type, similar to a typedef in C, however
+ it genuinely creates a new type, and not an alias. There are
+ no implicit conversions, but a tyname will inherit all
+ constraints of its underlying type.
+
+ A typaram is a parametric type. It is used in generics as
+ a placeholder for a type that will be substituted in later.
+ It is an identifier prefixed with '@'. These are only valid
+ within generic contexts, and may not appear elsewhere.
+
+ A tyvar is an internal implementation detail that currently
+ leaks in error messages out during type inference, and is a
+ major cause of confusing error messages. It should not be in
+ this manual, except that the current incarnation of the
+ compiler will make you aware of it. It looks like '@$type',
+ and is a variable that holds an incompletely inferred type.
+
+ type mine = int creates a tyname named
+ 'mine', equivalent to int.
+
+
+ @foo creates a type parameter
+ named '@foo'.
+
+
+ 3.6.
+
+ The myrddin type system is a system similar to the Hindley Milner
+ system, however, types are not implicitly generalized. Instead, type
+ schemes (type parameters, in Myrddin lingo) must be explicitly provided
+ in the declarations. For purposes of brevity, instead of specifying type
+ rules for every operator, we group operators which behave identically
+ from the type system perspective into a small set of classes. and define
+ the constraints that they require.
+
+ Type inference in Myrddin operates as a bottom up tree walk,
+ applying the type equations for the operator to its arguments.
+ It begins by initializing all leaf nodes with the most specific
+ known type for them as follows:
+
+ 3.6.1 Types for leaf nodes:
+
+ Variable Type
+ ----------------------
+ var foo $t
+
+ A type variable is the most specific type for a declaration
+ or function without any specified type
+
+ var foo : t t
+
+ If a type is specified, that type is taken for the
+ declaration.
+
+ "asdf" byte[:]
+
+ String literals are byte arrays.
+
+
+ 'a' char
+
+ Char literals are of type 'char'
+
+ true bool
+ false bool
+
+ true/false are boolean literals
+
+ 123 $t::(tcint,tcnum,tctest)
+
+ Integer literals get a fresh type variable of type with
+ the constraints for int-like types.
+
+ 123.1 $t::(tcfloat,tcnum,tctest)
+
+ Float literals get a fresh type variable of type with
+ the constraints for float-like types.
+
+ {a,b:t; } ($a,t -> $b)
+
+ Function literals get the most specific type that can
+ be determined by their signature.
+
+
+ num-binop:
+
+ + - * / %
+ += -= *= /= %
+
+ Number binops require the constraint 'tcnum' for both the
+
+ num-unary:
+ - +
+ Number binops require the constraint 'tcnum'.
+
+ int-binop:
+ | & ^ << >>
+ |= &= ^= <<= >>
+ int-unary:
+ ~ ++ --
+
+ bool-binop:
+ || && == !=
+ < <= > >=
+
+
+ 3.7. Packages and Uses:
+
+ pkg use
+
+ There are two keywords for module system. 'use' is the simpler
+ of the two, and has two cases:
+
+ use syspkg
+ use "localfile"
+
+ The unquoted form searches all system include paths for 'syspkg'
+ and imports it into the namespace. By convention, the namespace
+ defined by 'syspkg' is 'syspkg', and is unique and unmerged. This
+ is not enforced, however. Typical usage of unquoted names is to
+ import a library that already exists.
+
+ The quoted form searches the local directory for "localpkg". By
+ convention, the package it imports does not match the name
+ "localpkg", but instead is used as partial of the definition of the
+ importer's package. This is a confusing description.
+
+ A typical use of a quoted import is to allow splitting one package
+ into multiple files. In order to support this behavior, if a package
+ is defined in the current file, and a use statements imports a
+ package with the same namespace, the two namespaces are merged.
+
+ The 'pkg' keyword allows you to define a (partial) package by
+ listing the symbols and types for export. For example,
+
+ pkg mypkg =
+ type mytype
+
+ const Myconst : int = 42
+ const myfunc : (v : int -> bool)
+ ;;
+
+ declares a package "mypkg", which defines three exports, "mytype",
+ "Myconst", and "myfunc". The definitions of the values may be
+ defined in the 'pkg' specification, but it is preferred to implement
+ them in the body of the code for readability. Scanning the export
+ list is desirable from a readability perspective.
+
+4. TOOLCHAIN:
+
+ The toolchain used is inspired by the Plan 9 toolchain in name. There
+ is currently one compiler for x64, called '6m'. This compiler outputs
+ standard elf .o files, and supports these options:
+
+ 6m [-h] [-o outfile] [-d[dbgopts]] inputs
+ -I path Add 'path' to use search path
+ -o Output to outfile
+
+5. EXAMPLES:
+
+ 5.1. Hello World:
+
+ use std
+ const main = {
+ std.put("Hello World!\n")
+ -> 0
+ }
+
+ TODO: DESCRIBE CONSTRUCTS.
+
+ 5.2. Conditions
+
+ use std
+ const intmax = {a, b
+ if a > b
+ -> a
+ else
+ -> b
+ ;;
+ }
+
+ const main = {
+ var x = 123
+ var y = 456
+ std.put("The max of %i, %i is %i\n", x, y, max(x, y))
+ }
+
+ TODO: DESCRIBE CONSTRUCTS.
+
+ 5.3. Looping
+
+ use std
+ const innerprod = {a, b
+ var i
+ var sum
+ for i = 0; i < a.len; i++
+ sum += a[i]*b[i]
+ ;;
+ }
+
+ const main = {
+ std.put("The inner product is %i\n", innerprod([1,2,3], [4,5,6]))
+ }
+
+ TODO: DESCRIBE CONSTRUCTS.
+
+6. STYLE GUIDE:
+
+ 6.1. Brevity:
+
+ Myrddin is a simple language which aims to strip away abstraction when
+ possible, and it is not well served by overly abstract or bulky code.
+ The code written should be a readable description of an algorithm,
+ aimed at conveying the essential operations in a linear and
+ straightforward fasion.
+
+ Write for humans, not machines. Write linearly, so that an algorithm
+ can be understood with minimal function-chasing.
+
+ 6.2. Naming:
+
+ Names should be brief and evocative. A good name serves as a reminder
+ to what the function does. For functions, a single verb is ideal. For
+ local variables, a single character might suffice. Compact notation
+ is simpler to read, typographically.
+
+ Variables names should describe the value contained, and function
+ names should describe the value returned.
+
+ Good: spawn(myfunc)
+ Bad: create_new_thread_starting_at_function(myfunc)
+
+ The identifiers used for constant values are put in Initialcase.
+ Functions and types are in singleword style, although underscores are
+ occasionally necessary to specify additional information within
+ functions, due to the lack of overloading.
+
+ Good:
+ type mytype = int
+ var myvar : mytype
+ const Myconst = 42
+ union
+ `Tagone int
+ ;;
+
+ Bad:
+ type MyType = int /* types are 'singleword' */
+ const my_func = {;...} /* function names should avoid _ */
+ const myconst /* constants start with Uppercase */
+ union
+ `sometag /* tags start with uppercase */
+ ;;
+
+ Acceptable:
+ const length_mm = {;...} /* '_' disambiguates returned values. */
+ cosnt length_cm = {;...}
+
+ 6.3. Collections:
+
+
+
+7. STANDARD LIBRARY:
+
+8. GRAMMAR:
+
+9. FUTURE DIRECTIONS:
+
+BUGS:
+
+[1] TODO: trait, default, protect,
+[2] TODO: exponential notation.
+[3] TODO: \uDDDD escape sequences not yet recognized
+[4] TODO: currently the only sequence literal implemented is the
+ unindexed one
+
--- /dev/null
+++ b/doc/mc.1
@@ -1,0 +1,84 @@
+.TH MC 1
+.SH NAME
+6m
+.SH SYNOPSIS
+.B 6m
+.I -[hioS]
+.I [file...]
+.br
+.SH DESCRIPTION
+.PP
+The ?m family of compilers compile Myrddin source into object files
+for the corresponding architecture. There is one compiler for each
+architecture supported, with a unique name. By default, if the input
+file is named
+.I filename.myr
+then the the object file that is generated will be named
+.I filename.o.
+If the filename does not end with the suffix
+.I .myr
+then the suffix
+.I .o
+will simply be appended to it.
+
+.PP
+The following architectures are currently supported:
+.TP
+6m
+x86-64
+
+.PP
+The compiler options are:
+
+.TP
+.B -d [flTri]
+Print debugging dumps. Additional options may be given to give more
+debugging information for specific intermediate states of the compilation.
+
+.TP
+.B -h
+Print a summary of the available options.
+
+.TP
+.B -I path
+Add 'path' to the search path for unquoted use statments. This option
+does not affect the search path for local usefiles, which are always
+searched relative to the compiler's current working directory. Without
+any options, the search path defaults to /usr/include/myr.
+
+.TP
+.B -o output-file
+Specify that the generated code should be placed in
+
+.TP
+.B -S
+Generate assembly code instead of an object file.
+
+.SH EXAMPLE
+.EX
+ 6m foo.myr
+ 6m bar.myr
+ ld -o foobar foo.o bar.o
+.EE
+
+.SH FILES
+The source code for this compiler is available from
+.B git://git.eigenstate.org/git/ori/mc.git
+
+.SH SEE ALSO
+.IR muse(1)
+.IR ld(1)
+.IR as(1)
+
+.SH BUGS
+.PP
+The language is not yet complete, and the compilers often omit useful
+constructs in spite of their desirability.
+.PP
+There are virtually no optimizations done, and the generated source is
+often very poorly performing.
+.PP
+The current calling convention is stack-based and not register-based, even
+on architectures where it should be register-based.
+.PP
+The calling convention is not compatible with C.
--- /dev/null
+++ b/doc/muse.1
@@ -1,0 +1,95 @@
+.TH MUSE 1
+.SH NAME
+muse
+.SH SYNOPSIS
+.B muse
+.I -[hmidos]
+.I [file...]
+.br
+.SH DESCRIPTION
+.PP
+The 'muse' tool takes as input a Myrddin source file and generates
+a usefile from it. A usefile collects definitions exported from the
+package specifications in Myrddin source code, and makes them available
+for other programs to include with a 'use' statement.
+.PP
+It can also merge together a number of usefiles into one larger usefile
+including all of the exported symbols. If an output file name is not given,
+and we are not merging usefiles, then an input file named
+.I filename.myr
+will generate a usefile named
+.I filename.use
+\&.
+
+If the filename does not end with the suffix
+.I .myr
+then the suffix
+.I .o
+will simply be appended to it.
+
+.PP
+The output of muse is architecture-independent. However, the format of the
+generated file is not stable, and is not guaranteed to work across
+different compiler versions.
+
+.PP
+The muse options are:
+
+.TP
+.B -d [flTri]
+Print debugging dumps. Additional options may be given to give more
+debugging information for specific intermediate states of the compilation.
+
+.TP
+.B -h
+Print a summary of the available options.
+
+.TP
+.B -I path
+Add 'path' to the search path for unquoted use statments. This option
+does not affect the search path for local usefiles, which are always
+searched relative to the compiler's current working directory. Without
+any options, the search path defaults to /usr/include/myr.
+
+.TP
+.B -o output-file
+Specify that the generated usefile should be named
+.I output-file
+
+.TP
+.B -s
+Print a summary of the symbols exported from the usefile that is specified.
+
+.SH EXAMPLE
+.EX
+ muse foo.myr
+ muse -o bar.use bar-system-version.myr
+ muse -mo library foo.use bar.use
+.EE
+
+.SH FILES
+The source for muse is available from
+.B git://git.eigenstate.org/git/ori/mc.git
+and lives in the
+.I muse/
+directory within the source tree.
+
+.SH SEE ALSO
+.IR mc(1)
+.IR ld(1)
+.IR as(1)
+
+.SH BUGS
+.PP
+There is insufficient checking and validation done on usefiles.
+.PP
+The file format is in flux, and in current iterations, it is not at all compact.
+.PP
+There is no versioning on the usefiles as it stands. If you use the wrong
+version with the wrong compiler, a mysterious error or even segfault is
+likely.
+.PP
+This utility should not exist. Instead, the compiler should put the
+exported symbol information into the object file or library directly.
+.PP
+The file format is not closed under concatentation.
--- /dev/null
+++ b/doc/myrbuild.1
@@ -1,0 +1,66 @@
+.TH MUSE 1
+.SH NAME
+myrbuild
+.SH SYNOPSIS
+.B myrbuild
+.I -[hblI]
+.I [file...]
+.br
+.SH DESCRIPTION
+.PP
+The 'myrbuild' tool takes as input a list of Myrddin or assembly sources,
+and compiles them in the correct dependency order into either a library or
+an executable. If the source files are myrddin sources, then the appropriate
+usefiles will be created as well. It expects Myrddin source to be in '.myr'
+files.
+
+.PP
+Myrbuild will default to building for the current architecture.
+
+.PP
+The myrbuild options are:
+
+.TP
+.B -h
+Print a summary of the available options.
+
+.TP
+.B -b name
+Compile source into a binary named 'name'. If neither this option nor
+the '-l' option are given, myrbuild will create a binary called 'a.out'.
+
+.TP
+.B -l 'name'
+Compile source given into a library called 'lib<name>.a', and a matching
+usefile called 'name'. Only static libraries are currently supported.
+
+.TP
+.B -I path
+Add 'path' to the search path for unquoted use statments. This option
+does not affect the search path for local usefiles, which are always
+searched relative to the compiler's current working directory. Without
+any options, the search path defaults to /usr/include/myr.
+
+.SH EXAMPLE
+.EX
+ myrbuild -b foo foo.myr
+ myrbuild -l foo bar.myr baz.myr
+ muse -mo library foo.use bar.use
+.EE
+
+.SH FILES
+The source for muse is available from
+.B git://git.eigenstate.org/git/ori/mc.git
+and lives in the
+.I myrbuild/
+directory within the source tree.
+
+.SH SEE ALSO
+.IR mc(1)
+.IR muse(1)
+.IR ld(1)
+.IR as(1)
+
+.SH BUGS
+.PP
+None known.
--- /dev/null
+++ b/libstd/Makefile
@@ -1,0 +1,102 @@
+MYRLIB=std
+MYRSRC= \
+ alloc.myr \
+ bigint.myr \
+ bitset.myr \
+ blat.myr \
+ chartype.myr \
+ cmp.myr \
+ dial.myr \
+ die.myr \
+ endian.myr \
+ env.myr \
+ execvp.myr \
+ extremum.myr \
+ floatbits.myr \
+ fmt.myr \
+ hashfuncs.myr \
+ hasprefix.myr \
+ hassuffix.myr \
+ htab.myr \
+ ifreq.myr \
+ intparse.myr \
+ ipparse.myr \
+ mk.myr \
+ now.myr \
+ option.myr \
+ optparse.myr \
+ rand.myr \
+ resolve.myr \
+ result.myr \
+ search.myr \
+ slcp.myr \
+ sldup.myr \
+ sleq.myr \
+ slfill.myr \
+ sljoin.myr \
+ slpush.myr \
+ slput.myr \
+ slurp.myr \
+ sort.myr \
+ strfind.myr \
+ strjoin.myr \
+ strsplit.myr \
+ strstrip.myr \
+ sys.myr \
+ try.myr \
+ types.myr \
+ units.myr \
+ utf.myr \
+ varargs.myr \
+ waitstatus.myr \
+
+ASMSRC= \
+ start.s \
+ syscall.s \
+ util.s
+
+include ../config.mk
+
+all: lib$(MYRLIB).a $(MYRBIN) test
+
+sys.myr: sys-$(SYS).myr
+ cp sys-$(SYS).myr sys.myr
+
+ifreq.myr: ifreq-$(SYS).myr
+ cp ifreq-$(SYS).myr ifreq.myr
+
+waitstatus.myr: waitstatus-$(SYS).myr
+ cp waitstatus-$(SYS).myr waitstatus.myr
+
+syscall.s: syscall-$(SYS).s
+ cp syscall-$(SYS).s syscall.s
+
+start.s: start-$(SYS).s
+ cp start-$(SYS).s start.s
+
+test: libstd.a test.myr ../6/6m
+ ../myrbuild/myrbuild -C../6/6m -M../muse/muse -b test -I. test.myr
+
+
+lib$(MYRLIB).a: $(MYRSRC) $(ASMSRC) ../6/6m
+ ../myrbuild/myrbuild -C../6/6m -M../muse/muse -l $(MYRLIB) $(MYRSRC) $(ASMSRC)
+
+OBJ=$(MYRSRC:.myr=.o) $(ASMSRC:.s=.o)
+USE=$(MYRSRC:.myr=.use) $(MYRLIB)
+.PHONY: clean
+clean:
+ rm -f $(OBJ) test.o
+ rm -f $(USE) test.use
+ rm -f lib$(MYRLIB).a
+
+install: all
+ mkdir -p $(abspath $(DESTDIR)/$(INST_ROOT)/lib/myr)
+ install libstd.a $(abspath $(DESTDIR)/$(INST_ROOT)/lib/myr)
+ install std $(abspath $(DESTDIR)/$(INST_ROOT)/lib/myr)
+
+uninstall:
+ rm -f $(abspath $(DESTDIR)/$(INST_ROOT)/lib/myr/libstd.a)
+ rm -f $(abspath $(DESTDIR)/$(INST_ROOT)/lib/myr/std)
+
+../6/6m:
+ cd ..; make
--- /dev/null
+++ b/libstd/alloc.myr
@@ -1,0 +1,409 @@
+use "die.use"
+use "extremum.use"
+use "sys.use"
+use "types.use"
+use "units.use"
+
+/*
+The allocator implementation here is based on Bonwick's slab allocator.
+
+For small allocations (up to Bktmax), it works by requesting large,
+power of two aligned chunks from the operating system, and breaking
+them into a linked list of equal sized chunks. Allocations are then
+satisfied by taking the head of the list of chunks. Empty slabs
+are removed from the freelist.
+
+The data structure looks something like this:
+ Bkts:
+ [16 byte] -> [slab hdr | chunk -> chunk -> chunk] -> [slab hdr | chunk -> chunk -> chunk]
+ [32 byte] -> Zslab
+ [64 byte] -> [slab hdr | chunk -> chunk]
+ ...
+ [32k byte] -> ...
+
+Large allocations are simply satisfied by mmap().
+
+*/
+
+pkg std =
+ generic alloc : ( -> @a#)
+ generic zalloc : ( -> @a#)
+ generic free : (v:@a# -> void)
+
+ generic slalloc : (len : size -> @a[:])
+ generic slzalloc : (len : size -> @a[:])
+ generic slgrow : (sl : @a[:], len : size -> @a[:])
+ generic slzgrow : (sl : @a[:], len : size -> @a[:])
+ generic slfree : (sl : @a[:] -> void)
+
+ const bytealloc : (sz:size -> byte#)
+ const zbytealloc : (sz:size -> byte#)
+ const bytefree : (m:byte#, sz:size -> void)
+
+
+;;
+extern const put : (b : byte[:], args : ... -> size)
+
+/* null pointers. only used internally. */
+const Zbyteptr = 0 castto(byte#)
+const Zslab = 0 castto(slab#)
+const Zchunk = 0 castto(chunk#)
+
+const Slabsz = 1*MiB /* 1 meg slabs */
+const Cachemax = 16 /* maximum number of slabs in the cache */
+const Bktmax = 32*KiB /* Slabsz / 8; a balance. */
+const Pagesz = 4*KiB
+const Align = 16 /* minimum allocation alignment */
+
+var buckets : bucket[32] /* excessive */
+var initdone : int
+
+type slheader = struct
+ cap : size /* capacity in bytes */
+ magic : size /* magic check value */
+;;
+
+type bucket = struct
+ sz : size /* aligned size */
+ nper : size /* max number of elements per slab */
+ slabs : slab# /* partially filled or free slabs */
+ cache : slab# /* cache of empty slabs, to prevent thrashing */
+ ncache : size /* size of cache */
+;;
+
+type slab = struct
+ head : byte# /* head of virtual addresses, so we don't leak address space */
+ next : slab# /* the next slab on the chain */
+ freehd : chunk# /* the nodes we're allocating */
+ nfree : size /* the number of free nodes */
+;;
+
+type chunk = struct /* NB: must be smaller than sizeof(slab) */
+ next : chunk# /* the next chunk in the free list */
+;;
+
+/* Allocates an object of type @a, returning a pointer to it. */
+generic alloc = {-> @a#
+ -> bytealloc(sizeof(@a)) castto(@a#)
+}
+
+generic zalloc = {-> @a#
+ -> zbytealloc(sizeof(@a)) castto(@a#)
+}
+
+/* Frees a value of type @a */
+generic free = {v:@a# -> void
+ bytefree(v castto(byte#), sizeof(@a))
+}
+
+/* allocates a slice of 'len' elements. */
+generic slalloc = {len
+ var p, sz
+
+ if len == 0
+ -> [][:]
+ ;;
+ sz = len*sizeof(@a) + align(sizeof(slheader), Align)
+ p = bytealloc(sz)
+ p = inithdr(p, sz)
+ -> (p castto(@a#))[0:len]
+}
+
+generic slzalloc = {len
+ var p, sz
+
+ if len == 0
+ -> [][:]
+ ;;
+ sz = len*sizeof(@a) + align(sizeof(slheader), Align)
+ p = zbytealloc(sz)
+ p = inithdr(p, sz)
+ -> (p castto(@a#))[0:len]
+}
+
+const inithdr = {p, sz
+ var phdr, prest
+
+ phdr = p castto(slheader#)
+ phdr.cap = allocsz(sz) - align(sizeof(slheader), Align)
+ phdr.magic = (0xdeadbeefbadf00d castto(size))
+
+ prest = (p castto(size)) + align(sizeof(slheader), Align)
+ -> prest castto(byte#)
+}
+
+const checkhdr = {p
+ var phdr, addr
+
+ addr = p castto(size)
+ addr -= align(sizeof(slheader), Align)
+ phdr = addr castto(slheader#)
+ assert(phdr.magic == (0xdeadbeefbadf00d castto(size)), "corrupt memory\n")
+}
+
+/* Frees a slice */
+generic slfree = {sl
+ var head
+
+ if sl.len == 0
+ ->
+ ;;
+
+ checkhdr(sl castto(byte#))
+ head = (sl castto(byte#)) castto(size)
+ head -= align(sizeof(slheader), Align)
+ bytefree(head castto(byte#), slcap(sl castto(byte#)))
+}
+
+/* Grows a slice */
+generic slgrow = {sl : @a[:], len
+ var i, n
+ var new
+
+ /* if the slice doesn't need a bigger bucket, we don't need to realloc. */
+ if sl.len > 0 && slcap(sl castto(byte#)) >= allocsz(len*sizeof(@a))
+ -> (sl castto(@a#))[:len]
+ ;;
+
+ new = slalloc(len)
+ n = min(len, sl.len)
+ for i = 0; i < n; i++
+ new[i] = sl[i]
+ ;;
+ if sl.len > 0
+ slfree(sl)
+ ;;
+ -> new
+}
+
+/* Grows a slice, filling new entries with zero bytes */
+generic slzgrow = {sl : @a[:], len
+ var oldsz
+
+ oldsz = sl.len*sizeof(@a)
+ sl = slgrow(sl, len)
+ zfill((sl castto(byte#))[oldsz:len*sizeof(@a)])
+ -> sl
+}
+
+const slcap = {p
+ var phdr
+
+ phdr = (p castto(size)) - align(sizeof(slheader), Align) castto(slheader#)
+ -> phdr.cap
+}
+
+const zbytealloc = {sz
+ var p
+
+ p = bytealloc(sz)
+ zfill(p[0:sz])
+ -> p
+}
+
+const zfill = {sl
+ var i
+
+ for i = 0; i < sl.len; i++
+ sl[i] = 0
+ ;;
+}
+
+/* Allocates a blob that is 'sz' bytes long. Dies if the allocation fails */
+const bytealloc = {sz
+ var i, bkt
+
+ if !initdone
+ for i = 0; i < buckets.len && (Align << i) <= Bktmax; i++
+ bktinit(&buckets[i], Align << i)
+ ;;
+ initdone = 1
+ ;;
+
+ if (sz <= Bktmax)
+ bkt = &buckets[bktnum(sz)]
+ -> bktalloc(bkt)
+ else
+ -> mmap(Zbyteptr, sz, Mprotrw, Mpriv | Manon, -1, 0)
+ ;;
+}
+
+/* frees a blob that is 'sz' bytes long. */
+const bytefree = {m, sz
+ var bkt
+
+ if (sz < Bktmax)
+ bkt = &buckets[bktnum(sz)]
+ bktfree(bkt, m)
+ else
+ munmap(m, sz)
+ ;;
+}
+
+/* Sets up a single empty bucket */
+const bktinit = {b, sz
+ b.sz = align(sz, Align)
+ b.nper = (Slabsz - sizeof(slab))/b.sz
+ b.slabs = Zslab
+ b.cache = Zslab
+ b.ncache = 0
+}
+
+/* Creates a slab for bucket 'bkt', and fills the chunk list */
+const mkslab = {bkt
+ var i, p, s
+ var b, bnext
+ var off /* offset of chunk head */
+
+ if bkt.ncache > 0
+ s = bkt.cache
+ bkt.cache = s.next
+ bkt.ncache--
+ ;;
+ /* tricky: we need power of two alignment, so we allocate double the
+ needed size, chop off the unaligned ends, and waste the address
+ space. Since the OS is "smart enough", this shouldn't actually
+ cost us memory, and 64 bits of address space means that we're not
+ going to have issues with running out of address space for a
+ while. On a 32 bit system this would be a bad idea. */
+ p = mmap(Zbyteptr, Slabsz*2, Mprotrw, Mpriv | Manon, -1, 0)
+ if p == Mapbad
+ die("Unable to mmap")
+ ;;
+
+ s = align(p castto(size), Slabsz) castto(slab#)
+ s.head = p
+ s.nfree = bkt.nper
+ /* skip past the slab header */
+ off = align(sizeof(slab), Align)
+ bnext = nextchunk(s castto(chunk#), off)
+ s.freehd = bnext
+ for i = 0; i < bkt.nper; i++
+ b = bnext
+ bnext = nextchunk(b, bkt.sz)
+ b.next = bnext
+ ;;
+ b.next = Zchunk
+ -> s
+}
+
+/*
+Allocates a node from bucket 'bkt', crashing if the
+allocation cannot be satisfied. Will create a new slab
+if there are no slabs on the freelist.
+*/
+const bktalloc = {bkt
+ var s
+ var b
+
+ /* find a slab */
+ s = bkt.slabs
+ if s == Zslab
+ s = mkslab(bkt)
+ if s == Zslab
+ die("No memory left")
+ ;;
+ bkt.slabs = s
+ ;;
+
+ /* grab the first chunk on the slab */
+ b = s.freehd
+ s.freehd = b.next
+ s.nfree--
+ if !s.nfree
+ bkt.slabs = s.next
+ s.next = Zslab
+ ;;
+
+ -> b castto(byte#)
+}
+
+/*
+Frees a chunk of memory 'm' into bucket 'bkt'.
+Assumes that the memory already came from a slab
+that was created for bucket 'bkt'. Will crash
+if this is not the case.
+*/
+const bktfree = {bkt, m
+ var s, b
+
+ s = mtrunc(m, Slabsz) castto(slab#)
+ b = m castto(chunk#)
+ if s.nfree == 0
+ s.next = bkt.slabs
+ bkt.slabs = s
+ elif s.nfree == bkt.nper
+ if bkt.ncache < Cachemax
+ s.next = bkt.cache
+ bkt.cache = s
+ else
+ /* we mapped 2*Slabsz so we could align it,
+ so we need to unmap the same */
+ munmap(s.head, Slabsz*2)
+ ;;
+ ;;
+ s.nfree++
+ b.next = s.freehd
+ s.freehd = b
+}
+
+/*
+Finds the correct bucket index to allocate from
+for allocations of size 'sz'
+*/
+const bktnum = {sz
+ var i, bktsz
+
+ bktsz = Align
+ for i = 0; bktsz <= Bktmax; i++
+ if bktsz >= sz
+ -> i
+ ;;
+ bktsz *= 2
+ ;;
+ die("Size does not match any buckets")
+}
+
+/*
+returns the actual size we allocated for a given
+size request
+*/
+const allocsz = {sz
+ var i, bktsz
+
+ if sz <= Bktmax
+ bktsz = Align
+ for i = 0; bktsz <= Bktmax; i++
+ if bktsz >= sz
+ -> bktsz
+ ;;
+ bktsz *= 2
+ ;;
+ else
+ -> align(sz, Pagesz)
+ ;;
+}
+
+/*
+aligns a size to a requested alignment.
+'align' must be a power of two
+*/
+const align = {v, align
+ -> (v + align - 1) & ~(align - 1)
+}
+
+/*
+chunks are variable sizes, so we can't just
+index to get to the next one
+*/
+const nextchunk = {b, sz : size
+ -> ((b castto(intptr)) + (sz castto(intptr))) castto(chunk#)
+}
+
+/*
+truncates a pointer to 'align'. 'align' must
+be a power of two.
+*/
+const mtrunc = {m, align
+ -> ((m castto(intptr)) & ~((align castto(intptr)) - 1)) castto(byte#)
+}
--- /dev/null
+++ b/libstd/bigint.myr
@@ -1,0 +1,577 @@
+use "alloc.use"
+use "cmp.use"
+use "die.use"
+use "extremum.use"
+use "fmt.use"
+use "hasprefix.use"
+use "chartype.use"
+use "option.use"
+use "slcp.use"
+use "sldup.use"
+use "slfill.use"
+use "slpush.use"
+use "types.use"
+use "utf.use"
+
+pkg std =
+ type bigint = struct
+ dig : uint32[:] /* little endian, no leading zeros. */
+ sign : int /* -1 for -ve, 0 for zero, 1 for +ve. */
+ ;;
+
+ /* administrivia */
+ const mkbigint : (v : int32 -> bigint#)
+ const bigfree : (a : bigint# -> void)
+ const bigdup : (a : bigint# -> bigint#)
+ const bigassign : (d : bigint#, s : bigint# -> bigint#)
+ const bigparse : (s : byte[:] -> option(bigint#))
+ const bigfmt : (b : byte[:], a : bigint# -> size)
+
+ /* some useful predicates */
+ const bigiszero : (a : bigint# -> bool)
+ const bigcmp : (a : bigint#, b : bigint# -> order)
+
+ /* bigint*bigint -> bigint ops */
+ const bigadd : (a : bigint#, b : bigint# -> bigint#)
+ const bigsub : (a : bigint#, b : bigint# -> bigint#)
+ const bigmul : (a : bigint#, b : bigint# -> bigint#)
+ const bigdiv : (a : bigint#, b : bigint# -> bigint#)
+ const bigmod : (a : bigint#, b : bigint# -> bigint#)
+ const bigdivmod : (a : bigint#, b : bigint# -> [bigint#, bigint#])
+ const bigshl : (a : bigint#, b : bigint# -> bigint#)
+ const bigshr : (a : bigint#, b : bigint# -> bigint#)
+
+ /* bigint*int -> bigint ops */
+ const bigaddi : (a : bigint#, b : int64 -> bigint#)
+ const bigsubi : (a : bigint#, b : int64 -> bigint#)
+ const bigmuli : (a : bigint#, b : int64 -> bigint#)
+ const bigdivi : (a : bigint#, b : int64 -> bigint#)
+ const bigshli : (a : bigint#, b : uint64 -> bigint#)
+ const bigshri : (a : bigint#, b : uint64 -> bigint#)
+;;
+
+const Base = 0x100000000ul
+
+const mkbigint = {v
+ var a
+ a = zalloc()
+
+ a.dig = slalloc(1)
+ if v < 0
+ a.sign = -1
+ v = -v
+ elif v > 0
+ a.sign = 1
+ ;;
+ a.dig[0] = (v castto(uint32))
+ -> trim(a)
+}
+
+const bigfree = {a
+ slfree(a.dig)
+ free(a)
+}
+
+const bigdup = {a
+ -> bigassign(zalloc(), a)
+}
+
+const bigassign = {d, s
+ slfree(d.dig)
+ d.dig = sldup(s.dig)
+ d.sign = s.sign
+ -> d
+}
+
+
+/* for now, just dump out something for debugging... */
+const bigfmt = {buf, val
+ const digitchars = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
+ var v
+ var n, i
+ var tmp
+ var rem
+
+ n = 0
+ if val.sign == 0
+ n += encode(buf, '0')
+ elif val.sign == -1
+ n += encode(buf, '-')
+ ;;
+
+ val = bigdup(val)
+ /* generate the digits in reverse order */
+ while !bigiszero(val)
+ (v, rem) = bigdivmod(val, mkbigint(10))
+ if rem.dig.len > 0
+ n += bfmt(buf[n:], "%c", digitchars[rem.dig[0]])
+ else
+ n += bfmt(buf[n:], "0")
+ ;;
+ bigfree(val)
+ bigfree(rem)
+ val = v
+ ;;
+ bigfree(val)
+ /* we only generated ascii digits, so this works. ugh. */
+ for i = 0; i < n/2; i++
+ tmp = buf[i]
+ buf[i] = buf[n - i - 1]
+ buf[n - i - 1] = tmp
+ ;;
+ -> n
+}
+
+const bigparse = {str
+ var c, val, base
+ var v, b
+ var a
+
+ if hasprefix(str, "0x") || hasprefix(str, "0X")
+ base = 16
+ elif hasprefix(str, "0o") || hasprefix(str, "0O")
+ base = 8
+ elif hasprefix(str, "0b") || hasprefix(str, "0B")
+ base = 2
+ else
+ base = 10
+ ;;
+
+ a = mkbigint(0)
+ b = mkbigint(base castto(int32))
+ /*
+ efficiency hack: to save allocations,
+ just mutate v[0]. The value will always
+ fit in one digit.
+ */
+ v = mkbigint(1)
+ while str.len != 0
+ (c, str) = striter(str)
+ if c == '_'
+ continue
+ ;;
+ val = charval(c, base)
+ if val < 0
+ bigfree(a)
+ -> `None
+ ;;
+ v.dig[0] = val
+ if val == 0
+ v.sign = 0
+ else
+ v.sign = 1
+ ;;
+ bigmul(a, b)
+ bigadd(a, v)
+
+ ;;
+ -> `Some a
+}
+
+const bigiszero = {v
+ -> v.dig.len == 0
+}
+
+const bigcmp = {a, b
+ var i
+
+ if a.sign < b.sign
+ -> `Before
+ elif a.sign > b.sign
+ -> `After
+ else
+ /* the one with more digits has greater magnitude */
+ if a.dig.len > b.dig.len
+ -> signedorder(a.sign)
+ ;;
+ /* otherwise, the one with the first larger digit is bigger */
+ for i = a.dig.len; i > 0; i--
+ if a.dig[i - 1] > b.dig[i - 1]
+ -> signedorder(a.sign)
+ elif b.dig[i - 1] > a.dig[i - 1]
+ -> signedorder(a.sign)
+ ;;
+ ;;
+ ;;
+ -> `Equal
+}
+
+const signedorder = {sign
+ if sign < 0
+ -> `Before
+ else
+ -> `After
+ ;;
+}
+
+/* a += b */
+const bigadd = {a, b
+ if a.sign == b.sign || a.sign == 0
+ a.sign = b.sign
+ -> uadd(a, b)
+ elif b.sign == 0
+ -> a
+ else
+ match bigcmp(a, b)
+ | `Before: /* a is negative */
+ a.sign = b.sign
+ -> usub(b, a)
+ | `After: /* b is negative */
+ -> usub(a, b)
+ | `Equal:
+ die("Impossible. Equal vals with different sign.")
+ ;;
+ ;;
+}
+
+/* adds two unsigned values together. */
+const uadd = {a, b
+ var v, i
+ var carry
+ var n
+
+ carry = 0
+ n = max(a.dig.len, b.dig.len)
+ /* guaranteed to carry no more than one value */
+ a.dig = slzgrow(a.dig, n + 1)
+ for i = 0; i < n; i++
+ v = (a.dig[i] castto(uint64)) + (b.dig[i] castto(uint64)) + carry;
+ if v >= Base
+ carry = 1
+ else
+ carry = 0
+ ;;
+ a.dig[i] = v castto(uint32)
+ ;;
+ a.dig[i] += carry castto(uint32)
+ -> trim(a)
+}
+
+/* a -= b */
+const bigsub = {a, b
+ /* 0 - x = -x */
+ if a.sign == 0
+ bigassign(a, b)
+ a.sign = -b.sign
+ -> a
+ /* x - 0 = x */
+ elif b.sign == 0
+ -> a
+ elif a.sign != b.sign
+ -> uadd(a, b)
+ else
+ match bigcmp(a, b)
+ | `Before: /* a is negative */
+ a.sign = b.sign
+ -> usub(b, a)
+ | `After: /* b is negative */
+ -> usub(a, b)
+ | `Equal:
+ die("Impossible. Equal vals with different sign.")
+ ;;
+ ;;
+ -> a
+}
+
+/* subtracts two unsigned values, where 'a' is strictly greater than 'b' */
+const usub = {a, b
+ var carry
+ var v, i
+
+ carry = 0
+ for i = 0; i < a.dig.len; i++
+ v = (a.dig[i] castto(int64)) - (b.dig[i] castto(int64)) - carry
+ if v < 0
+ carry = 1
+ else
+ carry = 0
+ ;;
+ a.dig[i] = v castto(uint32)
+ ;;
+ -> trim(a)
+}
+
+/* a *= b */
+const bigmul = {a, b
+ var i, j
+ var ai, bj, wij
+ var carry, t
+ var w
+
+ if a.sign == 0 || b.sign == 0
+ a.sign = 0
+ slfree(a.dig)
+ a.dig = [][:]
+ -> a
+ elif a.sign != b.sign
+ a.sign = -1
+ else
+ a.sign = 1
+ ;;
+ w = slzalloc(a.dig.len + b.dig.len)
+ for j = 0; j < b.dig.len; j++
+ if a.dig[j] == 0
+ w[j] = 0
+ continue
+ ;;
+ carry = 0
+ for i = 0; i < a.dig.len; i++
+ ai = a.dig[i] castto(uint64)
+ bj = b.dig[j] castto(uint64)
+ wij = w[i+j] castto(uint64)
+ t = ai * bj + wij + carry
+ w[i + j] = (t castto(uint32))
+ carry = t >> 32
+ ;;
+ w[i+j] = carry castto(uint32)
+ ;;
+ slfree(a.dig)
+ a.dig = w
+ -> trim(a)
+}
+
+const bigdiv = {a : bigint#, b : bigint# -> bigint#
+ var q, r
+
+ (q, r) = bigdivmod(a, b)
+ bigfree(r)
+ slfree(a.dig)
+ a.dig = q.dig
+ free(q)
+ -> a
+}
+const bigmod = {a : bigint#, b : bigint# -> bigint#
+ var q, r
+
+ (q, r) = bigdivmod(a, b)
+ bigfree(q)
+ slfree(a.dig)
+ a.dig = r.dig
+ free(r)
+ -> a
+}
+
+/* a /= b */
+const bigdivmod = {a : bigint#, b : bigint# -> [bigint#, bigint#]
+ /*
+ Implements bigint division using Algorithm D from
+ Knuth: Seminumerical algorithms, Section 4.3.1.
+ */
+ var qhat, rhat, carry, shift
+ var x, y, z, w, p, t /* temporaries */
+ var b0, aj
+ var u, v
+ var m : int64, n : int64
+ var i, j : int64
+ var q
+
+ if bigiszero(b)
+ die("divide by zero\n")
+ ;;
+ /* if b > a, we trucate to 0, with remainder 'a' */
+ if a.dig.len < b.dig.len
+ -> (mkbigint(0), bigdup(a))
+ ;;
+
+ q = zalloc()
+ q.dig = slzalloc(max(a.dig.len, b.dig.len) + 1)
+ if a.sign != b.sign
+ q.sign = -1
+ else
+ q.sign = 1
+ ;;
+
+ /* handle single digit divisor separately: the knuth algorithm needs at least 2 digits. */
+ if b.dig.len == 1
+ carry = 0
+ b0 = (b.dig[0] castto(uint64))
+ for j = a.dig.len; j > 0; j--
+ aj = (a.dig[j - 1] castto(uint64))
+ q.dig[j - 1] = (((carry << 32) + aj)/b0) castto(uint32)
+ carry = (carry << 32) + aj - (q.dig[j-1] castto(uint64))*b0
+ ;;
+ -> (trim(q), trim(mkbigint(carry castto(int32))))
+ ;;
+
+ u = bigdup(a)
+ v = bigdup(b)
+ m = u.dig.len
+ n = v.dig.len
+
+ shift = nlz(v.dig[n - 1])
+ bigshli(u, shift)
+ bigshli(v, shift)
+ for j = m - n; j >= 0; j--
+ /* load a few temps */
+ x = u.dig[j + n] castto(uint64)
+ y = u.dig[j + n - 1] castto(uint64)
+ z = v.dig[n - 1] castto(uint64)
+ w = v.dig[n - 2] castto(uint64)
+ t = u.dig[j + n - 2] castto(uint64)
+
+ /* estimate qhat */
+ qhat = (x*Base + y)/z
+ rhat = (x*Base + y) - (qhat * z)
+:divagain
+ if qhat >= Base || (qhat * w) > (rhat*Base + t)
+ qhat--
+ rhat += z
+ if rhat < Base
+ goto divagain
+ ;;
+ ;;
+
+ /* multiply and subtract */
+ carry = 0
+ for i = 0; i < n; i++
+ p = qhat * (v.dig[i] castto(uint64))
+ t = (u.dig[i+j] castto(uint64)) - carry - (p % Base)
+ u.dig[i+j] = t castto(uint32)
+ carry = (((p castto(int64)) >> 32) - ((t castto(int64)) >> 32)) castto(uint64);
+ ;;
+ x = u.dig[j + n] castto(uint64)
+ t = x - carry
+ u.dig[j + n] = t castto(uint32)
+ q.dig[j] = qhat castto(uint32)
+ /* adjust */
+ if x < carry
+ q.dig[j]--
+ carry = 0
+ for i = 0; i < n; i++
+ t = (u.dig[i+j] castto(uint64)) + (v.dig[i] castto(uint64)) + carry
+ u.dig[i+j] = t castto(uint32)
+ carry = t >> 32
+ ;;
+ u.dig[j+n] = u.dig[j+n] + (carry castto(uint32));
+ ;;
+
+ ;;
+ /* undo the biasing for remainder */
+ bigshri(u, shift)
+ -> (trim(q), trim(u))
+}
+
+/* returns the number of leading zeros */
+const nlz = {a : uint32
+ var n
+
+ if a == 0
+ -> 32
+ ;;
+ n = 0
+ if a <= 0x0000ffff
+ n += 16
+ a <<= 16
+ ;;
+ if a <= 0x00ffffff
+ n += 8
+ a <<= 8
+ ;;
+ if a <= 0x0fffffff
+ n += 4
+ a <<= 4
+ ;;
+ if a <= 0x3fffffff
+ n += 2
+ a <<= 2
+ ;;
+ if a <= 0x7fffffff
+ n += 1
+ a <<= 1
+ ;;
+ -> n
+}
+
+
+/* a <<= b */
+const bigshl = {a, b
+ match b.dig.len
+ | 0: -> a
+ | 1: -> bigshli(a, b.dig[0] castto(uint64))
+ | n: die("shift by way too much\n")
+ ;;
+}
+
+/* a >>= b, unsigned */
+const bigshr = {a, b
+ match b.dig.len
+ | 0: -> a
+ | 1: -> bigshri(a, b.dig[0] castto(uint64))
+ | n: die("shift by way too much\n")
+ ;;
+}
+
+/*
+ a << s, with integer arg.
+ logical left shift. any other type would be illogical.
+ */
+const bigshli = {a, s
+ var off, shift
+ var t, carry
+ var i
+
+ off = s/32
+ shift = s % 32
+
+ a.dig = slzgrow(a.dig, 1 + a.dig.len + off castto(size))
+ /* blit over the base values */
+ for i = a.dig.len; i > off; i--
+ a.dig[i - 1] = a.dig[i - 1 - off]
+ ;;
+ for i = 0; i < off; i++
+ a.dig[i] = 0
+ ;;
+ /* and shift over by the remainder */
+ carry = 0
+ for i = 0; i < a.dig.len; i++
+ t = (a.dig[i] castto(uint64)) << shift
+ a.dig[i] = (t | carry) castto(uint32)
+ carry = t >> 32
+ ;;
+ -> trim(a)
+}
+
+/* logical shift right, zero fills. sign remains untouched. */
+const bigshri = {a, s
+ var off, shift
+ var t, carry
+ var i
+
+ off = s/32
+ shift = s % 32
+
+ /* blit over the base values */
+ for i = 0; i < a.dig.len - off; i++
+ a.dig[i] = a.dig[i + off]
+ ;;
+ for i = a.dig.len; i < a.dig.len + off; i++
+ a.dig[i] = 0
+ ;;
+ /* and shift over by the remainder */
+ carry = 0
+ for i = a.dig.len; i > 0; i--
+ t = (a.dig[i - 1] castto(uint64))
+ a.dig[i - 1] = (carry | (t >> shift)) castto(uint32)
+ carry = t << (32 - shift)
+ ;;
+ -> trim(a)
+}
+
+/* trims leading zeros */
+const trim = {a
+ var i
+
+ for i = a.dig.len; i > 0; i--
+ if a.dig[i - 1] != 0
+ break
+ ;;
+ ;;
+ a.dig = slgrow(a.dig, i)
+ if i == 0
+ a.sign = 0
+ elif a.sign == 0
+ a.sign = 1
+ ;;
+ -> a
+}
+
--- /dev/null
+++ b/libstd/bitset.myr
@@ -1,0 +1,142 @@
+use "alloc.use"
+use "extremum.use"
+use "mk.use"
+use "slfill.use"
+use "types.use"
+
+pkg std =
+ type bitset = struct
+ bits : size[:]
+ ;;
+
+ const mkbs : (-> bitset#)
+ const bsdup : (bs : bitset# -> bitset#)
+ const bsfree : (bs : bitset# -> void)
+
+ generic bsput : (bs : bitset#, v : @a::(integral,numeric) -> void)
+ generic bsdel : (bs : bitset#, v : @a::(integral,numeric) -> void)
+ generic bshas : (bs : bitset#, v : @a::(integral,numeric) -> bool)
+
+ const bsdiff : (a : bitset#, b : bitset# -> void)
+ const bsintersect : (a : bitset#, b : bitset# -> void)
+ const bsunion : (a : bitset#, b : bitset# -> void)
+ const bseq : (a : bitset#, b : bitset# -> bool)
+ const bsissub : (a : bitset#, b : bitset# -> bool)
+
+ const bsclear : (bs : bitset# -> bitset#)
+ const bscount : (bs : bitset# -> bitset#)
+ const bsiter : (bs : bitset# -> bitset#)
+;;
+
+const mkbs = {
+ -> zalloc()
+}
+
+const bsdup = {bs
+ -> mk([.bits=bs.bits])
+}
+
+const bsfree = {bs
+ slfree(bs.bits)
+ free(bs)
+}
+
+const bsclear = {bs
+ slfill(bs.bits, 0)
+ -> bs
+}
+
+generic bsput = {bs, v
+ var idx
+ var off
+
+ idx = (v castto(size)) / (8*sizeof(size))
+ off = (v castto(size)) % (8*sizeof(size))
+ ensurespace(bs, idx)
+ bs.bits[idx] |= (1 << off)
+}
+
+generic bsdel = {bs, v
+ var idx
+ var off
+
+ idx = (v castto(size)) / (8*sizeof(size))
+ off = (v castto(size)) % (8*sizeof(size))
+ ensurespace(bs, idx)
+ bs.bits[idx] &= ~(1 << off)
+}
+
+generic bshas = {bs, v
+ var idx
+ var off
+
+ idx = (v castto(size)) / (8*sizeof(size))
+ off = (v castto(size)) % (8*sizeof(size))
+ ensurespace(bs, idx)
+ -> (bs.bits[idx] & (1 << off)) != 0
+}
+
+const bsunion = {a, b
+ var i
+
+ eqsz(a, b)
+ for i = 0; i < b.bits.len; i++
+ a.bits[i] |= b.bits[i]
+ ;;
+}
+
+const bsintersect = {a, b
+ var i, n
+
+ n = min(a.bits.len, b.bits.len)
+ for i = 0; i < n; i++
+ a.bits[i] &= b.bits[i]
+ ;;
+}
+
+const bsdiff = {a, b
+ var i
+
+ ensurespace(b, a.bits.len)
+ for i = 0; i < a.bits.len; i++
+ a.bits[i] &= ~b.bits[i]
+ ;;
+}
+
+const bsissubset = {a, b
+ var i
+
+ eqsz(a, b);
+ for i = 0; i < a.bits.len; i++
+ if (b.bits[i] & a.bits[i]) != b.bits[i]
+ -> false
+ ;;
+ ;;
+ -> true
+}
+
+const bseq = {a, b
+ var i
+
+ eqsz(a, b)
+ for i = 0; i < a.bits.len; i++
+ if a.bits[i] != b.bits[i]
+ -> false
+ ;;
+ ;;
+ -> true
+}
+
+const ensurespace = {bs, v
+ if bs.bits.len <= v
+ bs.bits = slzgrow(bs.bits, v + 1)
+ ;;
+}
+
+const eqsz = {a, b
+ var sz
+
+ sz = max(a.bits.len, b.bits.len)
+ ensurespace(a, sz)
+ ensurespace(b, sz)
+}
--- /dev/null
+++ b/libstd/blat.myr
@@ -1,0 +1,28 @@
+use "sys.use"
+use "fmt.use"
+
+pkg std =
+ const blat : (path : byte[:], buf : byte[:] -> bool)
+;;
+
+const blat = {path, buf
+ var fd
+ var written
+ var n
+
+ fd = openmode(path, Ocreat|Owronly, 0o777)
+ if fd < 0
+ fatal(1, "Could not open file \"%s\"", path)
+ ;;
+
+ n = 0
+ while true
+ written = write(fd, buf[n:])
+ if written <= 0
+ goto done
+ ;;
+ n += written
+ ;;
+:done
+ -> written == 0
+}
--- /dev/null
+++ b/libstd/chartype.myr
@@ -1,0 +1,1227 @@
+use "die.use"
+use "sys.use"
+
+/*
+ Tables adapted from plan 9's runetype.c,
+ which lives in sys/src/libc/port/runetype.c
+*/
+
+pkg std =
+ /* predicates */
+ const isalpha : (c : char -> bool)
+ const isnum : (c : char -> bool)
+ const isalnum : (c : char -> bool)
+ const isspace : (c : char -> bool)
+ const isblank : (c : char -> bool)
+ const islower : (c : char -> bool)
+ const isupper : (c : char -> bool)
+ const istitle : (c : char -> bool)
+
+ /* transforms */
+ const tolower : (c : char -> char)
+ const toupper : (c : char -> char)
+ const totitle : (c : char -> char)
+
+ generic charval : (c : char, base : int -> @a::(integral,numeric))
+;;
+
+/*
+ * alpha ranges -
+ * only covers ranges not in lower||upper
+ */
+const ralpha2 = [
+ 0x00d8, 0x00f6, /* Ø - ö */
+ 0x00f8, 0x01f5, /* ø - ǵ */
+ 0x0250, 0x02a8, /* ɐ - ʨ */
+ 0x038e, 0x03a1, /* Ύ - Ρ */
+ 0x03a3, 0x03ce, /* Σ - ώ */
+ 0x03d0, 0x03d6, /* ϐ - ϖ */
+ 0x03e2, 0x03f3, /* Ϣ - ϳ */
+ 0x0490, 0x04c4, /* Ґ - ӄ */
+ 0x0561, 0x0587, /* ա - և */
+ 0x05d0, 0x05ea, /* א - ת */
+ 0x05f0, 0x05f2, /* װ - ײ */
+ 0x0621, 0x063a, /* ء - غ */
+ 0x0640, 0x064a, /* ـ - ي */
+ 0x0671, 0x06b7, /* ٱ - ڷ */
+ 0x06ba, 0x06be, /* ں - ھ */
+ 0x06c0, 0x06ce, /* ۀ - ێ */
+ 0x06d0, 0x06d3, /* ې - ۓ */
+ 0x0905, 0x0939, /* अ - ह */
+ 0x0958, 0x0961, /* क़ - ॡ */
+ 0x0985, 0x098c, /* অ - ঌ */
+ 0x098f, 0x0990, /* এ - ঐ */
+ 0x0993, 0x09a8, /* ও - ন */
+ 0x09aa, 0x09b0, /* প - র */
+ 0x09b6, 0x09b9, /* শ - হ */
+ 0x09dc, 0x09dd, /* ড় - ঢ় */
+ 0x09df, 0x09e1, /* য় - ৡ */
+ 0x09f0, 0x09f1, /* ৰ - ৱ */
+ 0x0a05, 0x0a0a, /* ਅ - ਊ */
+ 0x0a0f, 0x0a10, /* ਏ - ਐ */
+ 0x0a13, 0x0a28, /* ਓ - ਨ */
+ 0x0a2a, 0x0a30, /* ਪ - ਰ */
+ 0x0a32, 0x0a33, /* ਲ - ਲ਼ */
+ 0x0a35, 0x0a36, /* ਵ - ਸ਼ */
+ 0x0a38, 0x0a39, /* ਸ - ਹ */
+ 0x0a59, 0x0a5c, /* ਖ਼ - ੜ */
+ 0x0a85, 0x0a8b, /* અ - ઋ */
+ 0x0a8f, 0x0a91, /* એ - ઑ */
+ 0x0a93, 0x0aa8, /* ઓ - ન */
+ 0x0aaa, 0x0ab0, /* પ - ર */
+ 0x0ab2, 0x0ab3, /* લ - ળ */
+ 0x0ab5, 0x0ab9, /* વ - હ */
+ 0x0b05, 0x0b0c, /* ଅ - ଌ */
+ 0x0b0f, 0x0b10, /* ଏ - ଐ */
+ 0x0b13, 0x0b28, /* ଓ - ନ */
+ 0x0b2a, 0x0b30, /* ପ - ର */
+ 0x0b32, 0x0b33, /* ଲ - ଳ */
+ 0x0b36, 0x0b39, /* ଶ - ହ */
+ 0x0b5c, 0x0b5d, /* ଡ଼ - ଢ଼ */
+ 0x0b5f, 0x0b61, /* ୟ - ୡ */
+ 0x0b85, 0x0b8a, /* அ - ஊ */
+ 0x0b8e, 0x0b90, /* எ - ஐ */
+ 0x0b92, 0x0b95, /* ஒ - க */
+ 0x0b99, 0x0b9a, /* ங - ச */
+ 0x0b9e, 0x0b9f, /* ஞ - ட */
+ 0x0ba3, 0x0ba4, /* ண - த */
+ 0x0ba8, 0x0baa, /* ந - ப */
+ 0x0bae, 0x0bb5, /* ம - வ */
+ 0x0bb7, 0x0bb9, /* ஷ - ஹ */
+ 0x0c05, 0x0c0c, /* అ - ఌ */
+ 0x0c0e, 0x0c10, /* ఎ - ఐ */
+ 0x0c12, 0x0c28, /* ఒ - న */
+ 0x0c2a, 0x0c33, /* ప - ళ */
+ 0x0c35, 0x0c39, /* వ - హ */
+ 0x0c60, 0x0c61, /* ౠ - ౡ */
+ 0x0c85, 0x0c8c, /* ಅ - ಌ */
+ 0x0c8e, 0x0c90, /* ಎ - ಐ */
+ 0x0c92, 0x0ca8, /* ಒ - ನ */
+ 0x0caa, 0x0cb3, /* ಪ - ಳ */
+ 0x0cb5, 0x0cb9, /* ವ - ಹ */
+ 0x0ce0, 0x0ce1, /* ೠ - ೡ */
+ 0x0d05, 0x0d0c, /* അ - ഌ */
+ 0x0d0e, 0x0d10, /* എ - ഐ */
+ 0x0d12, 0x0d28, /* ഒ - ന */
+ 0x0d2a, 0x0d39, /* പ - ഹ */
+ 0x0d60, 0x0d61, /* ൠ - ൡ */
+ 0x0e01, 0x0e30, /* ก - ะ */
+ 0x0e32, 0x0e33, /* า - ำ */
+ 0x0e40, 0x0e46, /* เ - ๆ */
+ 0x0e5a, 0x0e5b, /* ๚ - ๛ */
+ 0x0e81, 0x0e82, /* ກ - ຂ */
+ 0x0e87, 0x0e88, /* ງ - ຈ */
+ 0x0e94, 0x0e97, /* ດ - ທ */
+ 0x0e99, 0x0e9f, /* ນ - ຟ */
+ 0x0ea1, 0x0ea3, /* ມ - ຣ */
+ 0x0eaa, 0x0eab, /* ສ - ຫ */
+ 0x0ead, 0x0eae, /* ອ - ຮ */
+ 0x0eb2, 0x0eb3, /* າ - ຳ */
+ 0x0ec0, 0x0ec4, /* ເ - ໄ */
+ 0x0edc, 0x0edd, /* ໜ - ໝ */
+ 0x0f18, 0x0f19, /* ༘ - ༙ */
+ 0x0f40, 0x0f47, /* ཀ - ཇ */
+ 0x0f49, 0x0f69, /* ཉ - ཀྵ */
+ 0x10d0, 0x10f6, /* ა - ჶ */
+ 0x1100, 0x1159, /* ᄀ - ᅙ */
+ 0x115f, 0x11a2, /* ᅟ - ᆢ */
+ 0x11a8, 0x11f9, /* ᆨ - ᇹ */
+ 0x1e00, 0x1e9b, /* Ḁ - ẛ */
+ 0x1f50, 0x1f57, /* ὐ - ὗ */
+ 0x1f80, 0x1fb4, /* ᾀ - ᾴ */
+ 0x1fb6, 0x1fbc, /* ᾶ - ᾼ */
+ 0x1fc2, 0x1fc4, /* ῂ - ῄ */
+ 0x1fc6, 0x1fcc, /* ῆ - ῌ */
+ 0x1fd0, 0x1fd3, /* ῐ - ΐ */
+ 0x1fd6, 0x1fdb, /* ῖ - Ί */
+ 0x1fe0, 0x1fec, /* ῠ - Ῥ */
+ 0x1ff2, 0x1ff4, /* ῲ - ῴ */
+ 0x1ff6, 0x1ffc, /* ῶ - ῼ */
+ 0x210a, 0x2113, /* ℊ - ℓ */
+ 0x2115, 0x211d, /* ℕ - ℝ */
+ 0x2120, 0x2122, /* ℠ - ™ */
+ 0x212a, 0x2131, /* K - ℱ */
+ 0x2133, 0x2138, /* ℳ - ℸ */
+ 0x3041, 0x3094, /* ぁ - ゔ */
+ 0x30a1, 0x30fa, /* ァ - ヺ */
+ 0x3105, 0x312c, /* ㄅ - ㄬ */
+ 0x3131, 0x318e, /* ㄱ - ㆎ */
+ 0x3192, 0x319f, /* ㆒ - ㆟ */
+ 0x3260, 0x327b, /* ㉠ - ㉻ */
+ 0x328a, 0x32b0, /* ㊊ - ㊰ */
+ 0x32d0, 0x32fe, /* ㋐ - ㋾ */
+ 0x3300, 0x3357, /* ㌀ - ㍗ */
+ 0x3371, 0x3376, /* ㍱ - ㍶ */
+ 0x337b, 0x3394, /* ㍻ - ㎔ */
+ 0x3399, 0x339e, /* ㎙ - ㎞ */
+ 0x33a9, 0x33ad, /* ㎩ - ㎭ */
+ 0x33b0, 0x33c1, /* ㎰ - ㏁ */
+ 0x33c3, 0x33c5, /* ㏃ - ㏅ */
+ 0x33c7, 0x33d7, /* ㏇ - ㏗ */
+ 0x33d9, 0x33dd, /* ㏙ - ㏝ */
+ 0x4e00, 0x9fff, /* 一 - 鿿 */
+ 0xac00, 0xd7a3, /* 가 - 힣 */
+ 0xf900, 0xfb06, /* 豈 - st */
+ 0xfb13, 0xfb17, /* ﬓ - ﬗ */
+ 0xfb1f, 0xfb28, /* ײַ - ﬨ */
+ 0xfb2a, 0xfb36, /* שׁ - זּ */
+ 0xfb38, 0xfb3c, /* טּ - לּ */
+ 0xfb40, 0xfb41, /* נּ - סּ */
+ 0xfb43, 0xfb44, /* ףּ - פּ */
+ 0xfb46, 0xfbb1, /* צּ - ﮱ */
+ 0xfbd3, 0xfd3d, /* ﯓ - ﴽ */
+ 0xfd50, 0xfd8f, /* ﵐ - ﶏ */
+ 0xfd92, 0xfdc7, /* ﶒ - ﷇ */
+ 0xfdf0, 0xfdf9, /* ﷰ - ﷹ */
+ 0xfe70, 0xfe72, /* ﹰ - ﹲ */
+ 0xfe76, 0xfefc, /* ﹶ - ﻼ */
+ 0xff66, 0xff6f, /* ヲ - ッ */
+ 0xff71, 0xff9d, /* ア - ン */
+ 0xffa0, 0xffbe, /* ᅠ - ᄒ */
+ 0xffc2, 0xffc7, /* ᅡ - ᅦ */
+ 0xffca, 0xffcf, /* ᅧ - ᅬ */
+ 0xffd2, 0xffd7, /* ᅭ - ᅲ */
+ 0xffda, 0xffdc /* ᅳ - ᅵ */
+]
+
+/*
+ * alpha singlets -
+ * only covers ranges not in lower||upper
+ */
+const ralpha1 = [
+ 0x00aa, /* ª */
+ 0x00b5, /* µ */
+ 0x00ba, /* º */
+ 0x03da, /* Ϛ */
+ 0x03dc, /* Ϝ */
+ 0x03de, /* Ϟ */
+ 0x03e0, /* Ϡ */
+ 0x06d5, /* ە */
+ 0x09b2, /* ল */
+ 0x0a5e, /* ਫ਼ */
+ 0x0a8d, /* ઍ */
+ 0x0ae0, /* ૠ */
+ 0x0b9c, /* ஜ */
+ 0x0cde, /* ೞ */
+ 0x0e4f, /* ๏ */
+ 0x0e84, /* ຄ */
+ 0x0e8a, /* ຊ */
+ 0x0e8d, /* ຍ */
+ 0x0ea5, /* ລ */
+ 0x0ea7, /* ວ */
+ 0x0eb0, /* ະ */
+ 0x0ebd, /* ຽ */
+ 0x1fbe, /* ι */
+ 0x207f, /* ⁿ */
+ 0x20a8, /* ₨ */
+ 0x2102, /* ℂ */
+ 0x2107, /* ℇ */
+ 0x2124, /* ℤ */
+ 0x2126, /* Ω */
+ 0x2128, /* ℨ */
+ 0xfb3e, /* מּ */
+ 0xfe74 /* ﹴ */
+]
+
+/*
+ * space ranges
+ */
+const rspace2 = [
+ 0x0009, 0x0009, /* tab */
+ 0x0020, 0x0020, /* space */
+ 0x0085, 0x0085,
+ 0x00a0, 0x00a0, /* */
+ 0x1680, 0x1680,
+ 0x180e, 0x180e,
+ 0x2000, 0x200b, /* - */
+ 0x2028, 0x2029, /* - */
+ 0x202f, 0x202f,
+ 0x205f, 0x205f,
+ 0x3000, 0x3000, /* */
+ 0xfeff, 0xfeff /* */
+]
+
+/*
+ * lower case ranges
+ * 3rd col is conversion excess 500
+ */
+const rtoupper2 = [
+ 0x0061, 0x007a, 468, /* a-z A-Z */
+ 0x00e0, 0x00f6, 468, /* à-ö À-Ö */
+ 0x00f8, 0x00fe, 468, /* ø-þ Ø-Þ */
+ 0x0256, 0x0257, 295, /* ɖ-ɗ Ɖ-Ɗ */
+ 0x0258, 0x0259, 298, /* ɘ-ə Ǝ-Ə */
+ 0x028a, 0x028b, 283, /* ʊ-ʋ Ʊ-Ʋ */
+ 0x03ad, 0x03af, 463, /* έ-ί Έ-Ί */
+ 0x03b1, 0x03c1, 468, /* α-ρ Α-Ρ */
+ 0x03c3, 0x03cb, 468, /* σ-ϋ Σ-Ϋ */
+ 0x03cd, 0x03ce, 437, /* ύ-ώ Ύ-Ώ */
+ 0x0430, 0x044f, 468, /* а-я А-Я */
+ 0x0451, 0x045c, 420, /* ё-ќ Ё-Ќ */
+ 0x045e, 0x045f, 420, /* ў-џ Ў-Џ */
+ 0x0561, 0x0586, 452, /* ա-ֆ Ա-Ֆ */
+ 0x1f00, 0x1f07, 508, /* ἀ-ἇ Ἀ-Ἇ */
+ 0x1f10, 0x1f15, 508, /* ἐ-ἕ Ἐ-Ἕ */
+ 0x1f20, 0x1f27, 508, /* ἠ-ἧ Ἠ-Ἧ */
+ 0x1f30, 0x1f37, 508, /* ἰ-ἷ Ἰ-Ἷ */
+ 0x1f40, 0x1f45, 508, /* ὀ-ὅ Ὀ-Ὅ */
+ 0x1f60, 0x1f67, 508, /* ὠ-ὧ Ὠ-Ὧ */
+ 0x1f70, 0x1f71, 574, /* ὰ-ά Ὰ-Ά */
+ 0x1f72, 0x1f75, 586, /* ὲ-ή Ὲ-Ή */
+ 0x1f76, 0x1f77, 600, /* ὶ-ί Ὶ-Ί */
+ 0x1f78, 0x1f79, 628, /* ὸ-ό Ὸ-Ό */
+ 0x1f7a, 0x1f7b, 612, /* ὺ-ύ Ὺ-Ύ */
+ 0x1f7c, 0x1f7d, 626, /* ὼ-ώ Ὼ-Ώ */
+ 0x1f80, 0x1f87, 508, /* ᾀ-ᾇ ᾈ-ᾏ */
+ 0x1f90, 0x1f97, 508, /* ᾐ-ᾗ ᾘ-ᾟ */
+ 0x1fa0, 0x1fa7, 508, /* ᾠ-ᾧ ᾨ-ᾯ */
+ 0x1fb0, 0x1fb1, 508, /* ᾰ-ᾱ Ᾰ-Ᾱ */
+ 0x1fd0, 0x1fd1, 508, /* ῐ-ῑ Ῐ-Ῑ */
+ 0x1fe0, 0x1fe1, 508, /* ῠ-ῡ Ῠ-Ῡ */
+ 0x2170, 0x217f, 484, /* ⅰ-ⅿ Ⅰ-Ⅿ */
+ 0x24d0, 0x24e9, 474, /* ⓐ-ⓩ Ⓐ-Ⓩ */
+ 0xff41, 0xff5a, 468 /* a-z A-Z */
+]
+
+/*
+ * lower case singlets
+ * 2nd col is conversion excess 500
+ */
+const rtoupper1 = [
+ 0x00ff, 621, /* ÿ Ÿ */
+ 0x0101, 499, /* ā Ā */
+ 0x0103, 499, /* ă Ă */
+ 0x0105, 499, /* ą Ą */
+ 0x0107, 499, /* ć Ć */
+ 0x0109, 499, /* ĉ Ĉ */
+ 0x010b, 499, /* ċ Ċ */
+ 0x010d, 499, /* č Č */
+ 0x010f, 499, /* ď Ď */
+ 0x0111, 499, /* đ Đ */
+ 0x0113, 499, /* ē Ē */
+ 0x0115, 499, /* ĕ Ĕ */
+ 0x0117, 499, /* ė Ė */
+ 0x0119, 499, /* ę Ę */
+ 0x011b, 499, /* ě Ě */
+ 0x011d, 499, /* ĝ Ĝ */
+ 0x011f, 499, /* ğ Ğ */
+ 0x0121, 499, /* ġ Ġ */
+ 0x0123, 499, /* ģ Ģ */
+ 0x0125, 499, /* ĥ Ĥ */
+ 0x0127, 499, /* ħ Ħ */
+ 0x0129, 499, /* ĩ Ĩ */
+ 0x012b, 499, /* ī Ī */
+ 0x012d, 499, /* ĭ Ĭ */
+ 0x012f, 499, /* į Į */
+ 0x0131, 268, /* ı I */
+ 0x0133, 499, /* ij IJ */
+ 0x0135, 499, /* ĵ Ĵ */
+ 0x0137, 499, /* ķ Ķ */
+ 0x013a, 499, /* ĺ Ĺ */
+ 0x013c, 499, /* ļ Ļ */
+ 0x013e, 499, /* ľ Ľ */
+ 0x0140, 499, /* ŀ Ŀ */
+ 0x0142, 499, /* ł Ł */
+ 0x0144, 499, /* ń Ń */
+ 0x0146, 499, /* ņ Ņ */
+ 0x0148, 499, /* ň Ň */
+ 0x014b, 499, /* ŋ Ŋ */
+ 0x014d, 499, /* ō Ō */
+ 0x014f, 499, /* ŏ Ŏ */
+ 0x0151, 499, /* ő Ő */
+ 0x0153, 499, /* œ Œ */
+ 0x0155, 499, /* ŕ Ŕ */
+ 0x0157, 499, /* ŗ Ŗ */
+ 0x0159, 499, /* ř Ř */
+ 0x015b, 499, /* ś Ś */
+ 0x015d, 499, /* ŝ Ŝ */
+ 0x015f, 499, /* ş Ş */
+ 0x0161, 499, /* š Š */
+ 0x0163, 499, /* ţ Ţ */
+ 0x0165, 499, /* ť Ť */
+ 0x0167, 499, /* ŧ Ŧ */
+ 0x0169, 499, /* ũ Ũ */
+ 0x016b, 499, /* ū Ū */
+ 0x016d, 499, /* ŭ Ŭ */
+ 0x016f, 499, /* ů Ů */
+ 0x0171, 499, /* ű Ű */
+ 0x0173, 499, /* ų Ų */
+ 0x0175, 499, /* ŵ Ŵ */
+ 0x0177, 499, /* ŷ Ŷ */
+ 0x017a, 499, /* ź Ź */
+ 0x017c, 499, /* ż Ż */
+ 0x017e, 499, /* ž Ž */
+ 0x017f, 200, /* ſ S */
+ 0x0183, 499, /* ƃ Ƃ */
+ 0x0185, 499, /* ƅ Ƅ */
+ 0x0188, 499, /* ƈ Ƈ */
+ 0x018c, 499, /* ƌ Ƌ */
+ 0x0192, 499, /* ƒ Ƒ */
+ 0x0199, 499, /* ƙ Ƙ */
+ 0x01a1, 499, /* ơ Ơ */
+ 0x01a3, 499, /* ƣ Ƣ */
+ 0x01a5, 499, /* ƥ Ƥ */
+ 0x01a8, 499, /* ƨ Ƨ */
+ 0x01ad, 499, /* ƭ Ƭ */
+ 0x01b0, 499, /* ư Ư */
+ 0x01b4, 499, /* ƴ Ƴ */
+ 0x01b6, 499, /* ƶ Ƶ */
+ 0x01b9, 499, /* ƹ Ƹ */
+ 0x01bd, 499, /* ƽ Ƽ */
+ 0x01c5, 499, /* Dž DŽ */
+ 0x01c6, 498, /* dž DŽ */
+ 0x01c8, 499, /* Lj LJ */
+ 0x01c9, 498, /* lj LJ */
+ 0x01cb, 499, /* Nj NJ */
+ 0x01cc, 498, /* nj NJ */
+ 0x01ce, 499, /* ǎ Ǎ */
+ 0x01d0, 499, /* ǐ Ǐ */
+ 0x01d2, 499, /* ǒ Ǒ */
+ 0x01d4, 499, /* ǔ Ǔ */
+ 0x01d6, 499, /* ǖ Ǖ */
+ 0x01d8, 499, /* ǘ Ǘ */
+ 0x01da, 499, /* ǚ Ǚ */
+ 0x01dc, 499, /* ǜ Ǜ */
+ 0x01df, 499, /* ǟ Ǟ */
+ 0x01e1, 499, /* ǡ Ǡ */
+ 0x01e3, 499, /* ǣ Ǣ */
+ 0x01e5, 499, /* ǥ Ǥ */
+ 0x01e7, 499, /* ǧ Ǧ */
+ 0x01e9, 499, /* ǩ Ǩ */
+ 0x01eb, 499, /* ǫ Ǫ */
+ 0x01ed, 499, /* ǭ Ǭ */
+ 0x01ef, 499, /* ǯ Ǯ */
+ 0x01f2, 499, /* Dz DZ */
+ 0x01f3, 498, /* dz DZ */
+ 0x01f5, 499, /* ǵ Ǵ */
+ 0x01fb, 499, /* ǻ Ǻ */
+ 0x01fd, 499, /* ǽ Ǽ */
+ 0x01ff, 499, /* ǿ Ǿ */
+ 0x0201, 499, /* ȁ Ȁ */
+ 0x0203, 499, /* ȃ Ȃ */
+ 0x0205, 499, /* ȅ Ȅ */
+ 0x0207, 499, /* ȇ Ȇ */
+ 0x0209, 499, /* ȉ Ȉ */
+ 0x020b, 499, /* ȋ Ȋ */
+ 0x020d, 499, /* ȍ Ȍ */
+ 0x020f, 499, /* ȏ Ȏ */
+ 0x0211, 499, /* ȑ Ȑ */
+ 0x0213, 499, /* ȓ Ȓ */
+ 0x0215, 499, /* ȕ Ȕ */
+ 0x0217, 499, /* ȗ Ȗ */
+ 0x0253, 290, /* ɓ Ɓ */
+ 0x0254, 294, /* ɔ Ɔ */
+ 0x025b, 297, /* ɛ Ɛ */
+ 0x0260, 295, /* ɠ Ɠ */
+ 0x0263, 293, /* ɣ Ɣ */
+ 0x0268, 291, /* ɨ Ɨ */
+ 0x0269, 289, /* ɩ Ɩ */
+ 0x026f, 289, /* ɯ Ɯ */
+ 0x0272, 287, /* ɲ Ɲ */
+ 0x0283, 282, /* ʃ Ʃ */
+ 0x0288, 282, /* ʈ Ʈ */
+ 0x0292, 281, /* ʒ Ʒ */
+ 0x03ac, 462, /* ά Ά */
+ 0x03cc, 436, /* ό Ό */
+ 0x03d0, 438, /* ϐ Β */
+ 0x03d1, 443, /* ϑ Θ */
+ 0x03d5, 453, /* ϕ Φ */
+ 0x03d6, 446, /* ϖ Π */
+ 0x03e3, 499, /* ϣ Ϣ */
+ 0x03e5, 499, /* ϥ Ϥ */
+ 0x03e7, 499, /* ϧ Ϧ */
+ 0x03e9, 499, /* ϩ Ϩ */
+ 0x03eb, 499, /* ϫ Ϫ */
+ 0x03ed, 499, /* ϭ Ϭ */
+ 0x03ef, 499, /* ϯ Ϯ */
+ 0x03f0, 414, /* ϰ Κ */
+ 0x03f1, 420, /* ϱ Ρ */
+ 0x0461, 499, /* ѡ Ѡ */
+ 0x0463, 499, /* ѣ Ѣ */
+ 0x0465, 499, /* ѥ Ѥ */
+ 0x0467, 499, /* ѧ Ѧ */
+ 0x0469, 499, /* ѩ Ѩ */
+ 0x046b, 499, /* ѫ Ѫ */
+ 0x046d, 499, /* ѭ Ѭ */
+ 0x046f, 499, /* ѯ Ѯ */
+ 0x0471, 499, /* ѱ Ѱ */
+ 0x0473, 499, /* ѳ Ѳ */
+ 0x0475, 499, /* ѵ Ѵ */
+ 0x0477, 499, /* ѷ Ѷ */
+ 0x0479, 499, /* ѹ Ѹ */
+ 0x047b, 499, /* ѻ Ѻ */
+ 0x047d, 499, /* ѽ Ѽ */
+ 0x047f, 499, /* ѿ Ѿ */
+ 0x0481, 499, /* ҁ Ҁ */
+ 0x0491, 499, /* ґ Ґ */
+ 0x0493, 499, /* ғ Ғ */
+ 0x0495, 499, /* ҕ Ҕ */
+ 0x0497, 499, /* җ Җ */
+ 0x0499, 499, /* ҙ Ҙ */
+ 0x049b, 499, /* қ Қ */
+ 0x049d, 499, /* ҝ Ҝ */
+ 0x049f, 499, /* ҟ Ҟ */
+ 0x04a1, 499, /* ҡ Ҡ */
+ 0x04a3, 499, /* ң Ң */
+ 0x04a5, 499, /* ҥ Ҥ */
+ 0x04a7, 499, /* ҧ Ҧ */
+ 0x04a9, 499, /* ҩ Ҩ */
+ 0x04ab, 499, /* ҫ Ҫ */
+ 0x04ad, 499, /* ҭ Ҭ */
+ 0x04af, 499, /* ү Ү */
+ 0x04b1, 499, /* ұ Ұ */
+ 0x04b3, 499, /* ҳ Ҳ */
+ 0x04b5, 499, /* ҵ Ҵ */
+ 0x04b7, 499, /* ҷ Ҷ */
+ 0x04b9, 499, /* ҹ Ҹ */
+ 0x04bb, 499, /* һ Һ */
+ 0x04bd, 499, /* ҽ Ҽ */
+ 0x04bf, 499, /* ҿ Ҿ */
+ 0x04c2, 499, /* ӂ Ӂ */
+ 0x04c4, 499, /* ӄ Ӄ */
+ 0x04c8, 499, /* ӈ Ӈ */
+ 0x04cc, 499, /* ӌ Ӌ */
+ 0x04d1, 499, /* ӑ Ӑ */
+ 0x04d3, 499, /* ӓ Ӓ */
+ 0x04d5, 499, /* ӕ Ӕ */
+ 0x04d7, 499, /* ӗ Ӗ */
+ 0x04d9, 499, /* ә Ә */
+ 0x04db, 499, /* ӛ Ӛ */
+ 0x04dd, 499, /* ӝ Ӝ */
+ 0x04df, 499, /* ӟ Ӟ */
+ 0x04e1, 499, /* ӡ Ӡ */
+ 0x04e3, 499, /* ӣ Ӣ */
+ 0x04e5, 499, /* ӥ Ӥ */
+ 0x04e7, 499, /* ӧ Ӧ */
+ 0x04e9, 499, /* ө Ө */
+ 0x04eb, 499, /* ӫ Ӫ */
+ 0x04ef, 499, /* ӯ Ӯ */
+ 0x04f1, 499, /* ӱ Ӱ */
+ 0x04f3, 499, /* ӳ Ӳ */
+ 0x04f5, 499, /* ӵ Ӵ */
+ 0x04f9, 499, /* ӹ Ӹ */
+ 0x1e01, 499, /* ḁ Ḁ */
+ 0x1e03, 499, /* ḃ Ḃ */
+ 0x1e05, 499, /* ḅ Ḅ */
+ 0x1e07, 499, /* ḇ Ḇ */
+ 0x1e09, 499, /* ḉ Ḉ */
+ 0x1e0b, 499, /* ḋ Ḋ */
+ 0x1e0d, 499, /* ḍ Ḍ */
+ 0x1e0f, 499, /* ḏ Ḏ */
+ 0x1e11, 499, /* ḑ Ḑ */
+ 0x1e13, 499, /* ḓ Ḓ */
+ 0x1e15, 499, /* ḕ Ḕ */
+ 0x1e17, 499, /* ḗ Ḗ */
+ 0x1e19, 499, /* ḙ Ḙ */
+ 0x1e1b, 499, /* ḛ Ḛ */
+ 0x1e1d, 499, /* ḝ Ḝ */
+ 0x1e1f, 499, /* ḟ Ḟ */
+ 0x1e21, 499, /* ḡ Ḡ */
+ 0x1e23, 499, /* ḣ Ḣ */
+ 0x1e25, 499, /* ḥ Ḥ */
+ 0x1e27, 499, /* ḧ Ḧ */
+ 0x1e29, 499, /* ḩ Ḩ */
+ 0x1e2b, 499, /* ḫ Ḫ */
+ 0x1e2d, 499, /* ḭ Ḭ */
+ 0x1e2f, 499, /* ḯ Ḯ */
+ 0x1e31, 499, /* ḱ Ḱ */
+ 0x1e33, 499, /* ḳ Ḳ */
+ 0x1e35, 499, /* ḵ Ḵ */
+ 0x1e37, 499, /* ḷ Ḷ */
+ 0x1e39, 499, /* ḹ Ḹ */
+ 0x1e3b, 499, /* ḻ Ḻ */
+ 0x1e3d, 499, /* ḽ Ḽ */
+ 0x1e3f, 499, /* ḿ Ḿ */
+ 0x1e41, 499, /* ṁ Ṁ */
+ 0x1e43, 499, /* ṃ Ṃ */
+ 0x1e45, 499, /* ṅ Ṅ */
+ 0x1e47, 499, /* ṇ Ṇ */
+ 0x1e49, 499, /* ṉ Ṉ */
+ 0x1e4b, 499, /* ṋ Ṋ */
+ 0x1e4d, 499, /* ṍ Ṍ */
+ 0x1e4f, 499, /* ṏ Ṏ */
+ 0x1e51, 499, /* ṑ Ṑ */
+ 0x1e53, 499, /* ṓ Ṓ */
+ 0x1e55, 499, /* ṕ Ṕ */
+ 0x1e57, 499, /* ṗ Ṗ */
+ 0x1e59, 499, /* ṙ Ṙ */
+ 0x1e5b, 499, /* ṛ Ṛ */
+ 0x1e5d, 499, /* ṝ Ṝ */
+ 0x1e5f, 499, /* ṟ Ṟ */
+ 0x1e61, 499, /* ṡ Ṡ */
+ 0x1e63, 499, /* ṣ Ṣ */
+ 0x1e65, 499, /* ṥ Ṥ */
+ 0x1e67, 499, /* ṧ Ṧ */
+ 0x1e69, 499, /* ṩ Ṩ */
+ 0x1e6b, 499, /* ṫ Ṫ */
+ 0x1e6d, 499, /* ṭ Ṭ */
+ 0x1e6f, 499, /* ṯ Ṯ */
+ 0x1e71, 499, /* ṱ Ṱ */
+ 0x1e73, 499, /* ṳ Ṳ */
+ 0x1e75, 499, /* ṵ Ṵ */
+ 0x1e77, 499, /* ṷ Ṷ */
+ 0x1e79, 499, /* ṹ Ṹ */
+ 0x1e7b, 499, /* ṻ Ṻ */
+ 0x1e7d, 499, /* ṽ Ṽ */
+ 0x1e7f, 499, /* ṿ Ṿ */
+ 0x1e81, 499, /* ẁ Ẁ */
+ 0x1e83, 499, /* ẃ Ẃ */
+ 0x1e85, 499, /* ẅ Ẅ */
+ 0x1e87, 499, /* ẇ Ẇ */
+ 0x1e89, 499, /* ẉ Ẉ */
+ 0x1e8b, 499, /* ẋ Ẋ */
+ 0x1e8d, 499, /* ẍ Ẍ */
+ 0x1e8f, 499, /* ẏ Ẏ */
+ 0x1e91, 499, /* ẑ Ẑ */
+ 0x1e93, 499, /* ẓ Ẓ */
+ 0x1e95, 499, /* ẕ Ẕ */
+ 0x1ea1, 499, /* ạ Ạ */
+ 0x1ea3, 499, /* ả Ả */
+ 0x1ea5, 499, /* ấ Ấ */
+ 0x1ea7, 499, /* ầ Ầ */
+ 0x1ea9, 499, /* ẩ Ẩ */
+ 0x1eab, 499, /* ẫ Ẫ */
+ 0x1ead, 499, /* ậ Ậ */
+ 0x1eaf, 499, /* ắ Ắ */
+ 0x1eb1, 499, /* ằ Ằ */
+ 0x1eb3, 499, /* ẳ Ẳ */
+ 0x1eb5, 499, /* ẵ Ẵ */
+ 0x1eb7, 499, /* ặ Ặ */
+ 0x1eb9, 499, /* ẹ Ẹ */
+ 0x1ebb, 499, /* ẻ Ẻ */
+ 0x1ebd, 499, /* ẽ Ẽ */
+ 0x1ebf, 499, /* ế Ế */
+ 0x1ec1, 499, /* ề Ề */
+ 0x1ec3, 499, /* ể Ể */
+ 0x1ec5, 499, /* ễ Ễ */
+ 0x1ec7, 499, /* ệ Ệ */
+ 0x1ec9, 499, /* ỉ Ỉ */
+ 0x1ecb, 499, /* ị Ị */
+ 0x1ecd, 499, /* ọ Ọ */
+ 0x1ecf, 499, /* ỏ Ỏ */
+ 0x1ed1, 499, /* ố Ố */
+ 0x1ed3, 499, /* ồ Ồ */
+ 0x1ed5, 499, /* ổ Ổ */
+ 0x1ed7, 499, /* ỗ Ỗ */
+ 0x1ed9, 499, /* ộ Ộ */
+ 0x1edb, 499, /* ớ Ớ */
+ 0x1edd, 499, /* ờ Ờ */
+ 0x1edf, 499, /* ở Ở */
+ 0x1ee1, 499, /* ỡ Ỡ */
+ 0x1ee3, 499, /* ợ Ợ */
+ 0x1ee5, 499, /* ụ Ụ */
+ 0x1ee7, 499, /* ủ Ủ */
+ 0x1ee9, 499, /* ứ Ứ */
+ 0x1eeb, 499, /* ừ Ừ */
+ 0x1eed, 499, /* ử Ử */
+ 0x1eef, 499, /* ữ Ữ */
+ 0x1ef1, 499, /* ự Ự */
+ 0x1ef3, 499, /* ỳ Ỳ */
+ 0x1ef5, 499, /* ỵ Ỵ */
+ 0x1ef7, 499, /* ỷ Ỷ */
+ 0x1ef9, 499, /* ỹ Ỹ */
+ 0x1f51, 508, /* ὑ Ὑ */
+ 0x1f53, 508, /* ὓ Ὓ */
+ 0x1f55, 508, /* ὕ Ὕ */
+ 0x1f57, 508, /* ὗ Ὗ */
+ 0x1fb3, 509, /* ᾳ ᾼ */
+ 0x1fc3, 509, /* ῃ ῌ */
+ 0x1fe5, 507, /* ῥ Ῥ */
+ 0x1ff3, 509 /* ῳ ῼ */
+]
+
+const rnums = [
+ 0x0030, 0x0039,
+ 0x0660, 0x0669,
+ 0x06f0, 0x06f9,
+ 0x07c0, 0x07c9,
+ 0x0966, 0x096f,
+ 0x09e6, 0x09ef,
+ 0x0a66, 0x0a6f,
+ 0x0ae6, 0x0aef,
+ 0x0b66, 0x0b6f,
+ 0x0be6, 0x0bef,
+ 0x0c66, 0x0c6f,
+ 0x0ce6, 0x0cef,
+ 0x0d66, 0x0d6f,
+ 0x0e50, 0x0e59,
+ 0x0ed0, 0x0ed9,
+ 0x0f20, 0x0f29,
+ 0x1040, 0x1049,
+ 0x17e0, 0x17e9,
+ 0x1810, 0x1819,
+ 0x1946, 0x194f,
+ 0x19d0, 0x19d9,
+ 0x1b50, 0x1b59,
+ 0xff10, 0xff19,
+ 0x104a0, 0x104a9,
+ 0x1d7ce, 0x1d7ff
+]
+
+/*
+ * upper case ranges
+ * 3rd col is conversion excess 500
+ */
+const rtolower2 = [
+ 0x0041, 0x005a, 532, /* A-Z a-z */
+ 0x00c0, 0x00d6, 532, /* À-Ö à-ö */
+ 0x00d8, 0x00de, 532, /* Ø-Þ ø-þ */
+ 0x0189, 0x018a, 705, /* Ɖ-Ɗ ɖ-ɗ */
+ 0x018e, 0x018f, 702, /* Ǝ-Ə ɘ-ə */
+ 0x01b1, 0x01b2, 717, /* Ʊ-Ʋ ʊ-ʋ */
+ 0x0388, 0x038a, 537, /* Έ-Ί έ-ί */
+ 0x038e, 0x038f, 563, /* Ύ-Ώ ύ-ώ */
+ 0x0391, 0x03a1, 532, /* Α-Ρ α-ρ */
+ 0x03a3, 0x03ab, 532, /* Σ-Ϋ σ-ϋ */
+ 0x0401, 0x040c, 580, /* Ё-Ќ ё-ќ */
+ 0x040e, 0x040f, 580, /* Ў-Џ ў-џ */
+ 0x0410, 0x042f, 532, /* А-Я а-я */
+ 0x0531, 0x0556, 548, /* Ա-Ֆ ա-ֆ */
+ 0x10a0, 0x10c5, 548, /* Ⴀ-Ⴥ ა-ჵ */
+ 0x1f08, 0x1f0f, 492, /* Ἀ-Ἇ ἀ-ἇ */
+ 0x1f18, 0x1f1d, 492, /* Ἐ-Ἕ ἐ-ἕ */
+ 0x1f28, 0x1f2f, 492, /* Ἠ-Ἧ ἠ-ἧ */
+ 0x1f38, 0x1f3f, 492, /* Ἰ-Ἷ ἰ-ἷ */
+ 0x1f48, 0x1f4d, 492, /* Ὀ-Ὅ ὀ-ὅ */
+ 0x1f68, 0x1f6f, 492, /* Ὠ-Ὧ ὠ-ὧ */
+ 0x1f88, 0x1f8f, 492, /* ᾈ-ᾏ ᾀ-ᾇ */
+ 0x1f98, 0x1f9f, 492, /* ᾘ-ᾟ ᾐ-ᾗ */
+ 0x1fa8, 0x1faf, 492, /* ᾨ-ᾯ ᾠ-ᾧ */
+ 0x1fb8, 0x1fb9, 492, /* Ᾰ-Ᾱ ᾰ-ᾱ */
+ 0x1fba, 0x1fbb, 426, /* Ὰ-Ά ὰ-ά */
+ 0x1fc8, 0x1fcb, 414, /* Ὲ-Ή ὲ-ή */
+ 0x1fd8, 0x1fd9, 492, /* Ῐ-Ῑ ῐ-ῑ */
+ 0x1fda, 0x1fdb, 400, /* Ὶ-Ί ὶ-ί */
+ 0x1fe8, 0x1fe9, 492, /* Ῠ-Ῡ ῠ-ῡ */
+ 0x1fea, 0x1feb, 388, /* Ὺ-Ύ ὺ-ύ */
+ 0x1ff8, 0x1ff9, 372, /* Ὸ-Ό ὸ-ό */
+ 0x1ffa, 0x1ffb, 374, /* Ὼ-Ώ ὼ-ώ */
+ 0x2160, 0x216f, 516, /* Ⅰ-Ⅿ ⅰ-ⅿ */
+ 0x24b6, 0x24cf, 526, /* Ⓐ-Ⓩ ⓐ-ⓩ */
+ 0xff21, 0xff3a, 532 /* A-Z a-z */
+]
+
+/*
+ * upper case singlets
+ * 2nd col is conversion excess 500
+ */
+const rtolower1 = [
+ 0x0100, 501, /* Ā ā */
+ 0x0102, 501, /* Ă ă */
+ 0x0104, 501, /* Ą ą */
+ 0x0106, 501, /* Ć ć */
+ 0x0108, 501, /* Ĉ ĉ */
+ 0x010a, 501, /* Ċ ċ */
+ 0x010c, 501, /* Č č */
+ 0x010e, 501, /* Ď ď */
+ 0x0110, 501, /* Đ đ */
+ 0x0112, 501, /* Ē ē */
+ 0x0114, 501, /* Ĕ ĕ */
+ 0x0116, 501, /* Ė ė */
+ 0x0118, 501, /* Ę ę */
+ 0x011a, 501, /* Ě ě */
+ 0x011c, 501, /* Ĝ ĝ */
+ 0x011e, 501, /* Ğ ğ */
+ 0x0120, 501, /* Ġ ġ */
+ 0x0122, 501, /* Ģ ģ */
+ 0x0124, 501, /* Ĥ ĥ */
+ 0x0126, 501, /* Ħ ħ */
+ 0x0128, 501, /* Ĩ ĩ */
+ 0x012a, 501, /* Ī ī */
+ 0x012c, 501, /* Ĭ ĭ */
+ 0x012e, 501, /* Į į */
+ 0x0130, 301, /* İ i */
+ 0x0132, 501, /* IJ ij */
+ 0x0134, 501, /* Ĵ ĵ */
+ 0x0136, 501, /* Ķ ķ */
+ 0x0139, 501, /* Ĺ ĺ */
+ 0x013b, 501, /* Ļ ļ */
+ 0x013d, 501, /* Ľ ľ */
+ 0x013f, 501, /* Ŀ ŀ */
+ 0x0141, 501, /* Ł ł */
+ 0x0143, 501, /* Ń ń */
+ 0x0145, 501, /* Ņ ņ */
+ 0x0147, 501, /* Ň ň */
+ 0x014a, 501, /* Ŋ ŋ */
+ 0x014c, 501, /* Ō ō */
+ 0x014e, 501, /* Ŏ ŏ */
+ 0x0150, 501, /* Ő ő */
+ 0x0152, 501, /* Œ œ */
+ 0x0154, 501, /* Ŕ ŕ */
+ 0x0156, 501, /* Ŗ ŗ */
+ 0x0158, 501, /* Ř ř */
+ 0x015a, 501, /* Ś ś */
+ 0x015c, 501, /* Ŝ ŝ */
+ 0x015e, 501, /* Ş ş */
+ 0x0160, 501, /* Š š */
+ 0x0162, 501, /* Ţ ţ */
+ 0x0164, 501, /* Ť ť */
+ 0x0166, 501, /* Ŧ ŧ */
+ 0x0168, 501, /* Ũ ũ */
+ 0x016a, 501, /* Ū ū */
+ 0x016c, 501, /* Ŭ ŭ */
+ 0x016e, 501, /* Ů ů */
+ 0x0170, 501, /* Ű ű */
+ 0x0172, 501, /* Ų ų */
+ 0x0174, 501, /* Ŵ ŵ */
+ 0x0176, 501, /* Ŷ ŷ */
+ 0x0178, 379, /* Ÿ ÿ */
+ 0x0179, 501, /* Ź ź */
+ 0x017b, 501, /* Ż ż */
+ 0x017d, 501, /* Ž ž */
+ 0x0181, 710, /* Ɓ ɓ */
+ 0x0182, 501, /* Ƃ ƃ */
+ 0x0184, 501, /* Ƅ ƅ */
+ 0x0186, 706, /* Ɔ ɔ */
+ 0x0187, 501, /* Ƈ ƈ */
+ 0x018b, 501, /* Ƌ ƌ */
+ 0x0190, 703, /* Ɛ ɛ */
+ 0x0191, 501, /* Ƒ ƒ */
+ 0x0193, 705, /* Ɠ ɠ */
+ 0x0194, 707, /* Ɣ ɣ */
+ 0x0196, 711, /* Ɩ ɩ */
+ 0x0197, 709, /* Ɨ ɨ */
+ 0x0198, 501, /* Ƙ ƙ */
+ 0x019c, 711, /* Ɯ ɯ */
+ 0x019d, 713, /* Ɲ ɲ */
+ 0x01a0, 501, /* Ơ ơ */
+ 0x01a2, 501, /* Ƣ ƣ */
+ 0x01a4, 501, /* Ƥ ƥ */
+ 0x01a7, 501, /* Ƨ ƨ */
+ 0x01a9, 718, /* Ʃ ʃ */
+ 0x01ac, 501, /* Ƭ ƭ */
+ 0x01ae, 718, /* Ʈ ʈ */
+ 0x01af, 501, /* Ư ư */
+ 0x01b3, 501, /* Ƴ ƴ */
+ 0x01b5, 501, /* Ƶ ƶ */
+ 0x01b7, 719, /* Ʒ ʒ */
+ 0x01b8, 501, /* Ƹ ƹ */
+ 0x01bc, 501, /* Ƽ ƽ */
+ 0x01c4, 502, /* DŽ dž */
+ 0x01c5, 501, /* Dž dž */
+ 0x01c7, 502, /* LJ lj */
+ 0x01c8, 501, /* Lj lj */
+ 0x01ca, 502, /* NJ nj */
+ 0x01cb, 501, /* Nj nj */
+ 0x01cd, 501, /* Ǎ ǎ */
+ 0x01cf, 501, /* Ǐ ǐ */
+ 0x01d1, 501, /* Ǒ ǒ */
+ 0x01d3, 501, /* Ǔ ǔ */
+ 0x01d5, 501, /* Ǖ ǖ */
+ 0x01d7, 501, /* Ǘ ǘ */
+ 0x01d9, 501, /* Ǚ ǚ */
+ 0x01db, 501, /* Ǜ ǜ */
+ 0x01de, 501, /* Ǟ ǟ */
+ 0x01e0, 501, /* Ǡ ǡ */
+ 0x01e2, 501, /* Ǣ ǣ */
+ 0x01e4, 501, /* Ǥ ǥ */
+ 0x01e6, 501, /* Ǧ ǧ */
+ 0x01e8, 501, /* Ǩ ǩ */
+ 0x01ea, 501, /* Ǫ ǫ */
+ 0x01ec, 501, /* Ǭ ǭ */
+ 0x01ee, 501, /* Ǯ ǯ */
+ 0x01f1, 502, /* DZ dz */
+ 0x01f2, 501, /* Dz dz */
+ 0x01f4, 501, /* Ǵ ǵ */
+ 0x01fa, 501, /* Ǻ ǻ */
+ 0x01fc, 501, /* Ǽ ǽ */
+ 0x01fe, 501, /* Ǿ ǿ */
+ 0x0200, 501, /* Ȁ ȁ */
+ 0x0202, 501, /* Ȃ ȃ */
+ 0x0204, 501, /* Ȅ ȅ */
+ 0x0206, 501, /* Ȇ ȇ */
+ 0x0208, 501, /* Ȉ ȉ */
+ 0x020a, 501, /* Ȋ ȋ */
+ 0x020c, 501, /* Ȍ ȍ */
+ 0x020e, 501, /* Ȏ ȏ */
+ 0x0210, 501, /* Ȑ ȑ */
+ 0x0212, 501, /* Ȓ ȓ */
+ 0x0214, 501, /* Ȕ ȕ */
+ 0x0216, 501, /* Ȗ ȗ */
+ 0x0386, 538, /* Ά ά */
+ 0x038c, 564, /* Ό ό */
+ 0x03e2, 501, /* Ϣ ϣ */
+ 0x03e4, 501, /* Ϥ ϥ */
+ 0x03e6, 501, /* Ϧ ϧ */
+ 0x03e8, 501, /* Ϩ ϩ */
+ 0x03ea, 501, /* Ϫ ϫ */
+ 0x03ec, 501, /* Ϭ ϭ */
+ 0x03ee, 501, /* Ϯ ϯ */
+ 0x0460, 501, /* Ѡ ѡ */
+ 0x0462, 501, /* Ѣ ѣ */
+ 0x0464, 501, /* Ѥ ѥ */
+ 0x0466, 501, /* Ѧ ѧ */
+ 0x0468, 501, /* Ѩ ѩ */
+ 0x046a, 501, /* Ѫ ѫ */
+ 0x046c, 501, /* Ѭ ѭ */
+ 0x046e, 501, /* Ѯ ѯ */
+ 0x0470, 501, /* Ѱ ѱ */
+ 0x0472, 501, /* Ѳ ѳ */
+ 0x0474, 501, /* Ѵ ѵ */
+ 0x0476, 501, /* Ѷ ѷ */
+ 0x0478, 501, /* Ѹ ѹ */
+ 0x047a, 501, /* Ѻ ѻ */
+ 0x047c, 501, /* Ѽ ѽ */
+ 0x047e, 501, /* Ѿ ѿ */
+ 0x0480, 501, /* Ҁ ҁ */
+ 0x0490, 501, /* Ґ ґ */
+ 0x0492, 501, /* Ғ ғ */
+ 0x0494, 501, /* Ҕ ҕ */
+ 0x0496, 501, /* Җ җ */
+ 0x0498, 501, /* Ҙ ҙ */
+ 0x049a, 501, /* Қ қ */
+ 0x049c, 501, /* Ҝ ҝ */
+ 0x049e, 501, /* Ҟ ҟ */
+ 0x04a0, 501, /* Ҡ ҡ */
+ 0x04a2, 501, /* Ң ң */
+ 0x04a4, 501, /* Ҥ ҥ */
+ 0x04a6, 501, /* Ҧ ҧ */
+ 0x04a8, 501, /* Ҩ ҩ */
+ 0x04aa, 501, /* Ҫ ҫ */
+ 0x04ac, 501, /* Ҭ ҭ */
+ 0x04ae, 501, /* Ү ү */
+ 0x04b0, 501, /* Ұ ұ */
+ 0x04b2, 501, /* Ҳ ҳ */
+ 0x04b4, 501, /* Ҵ ҵ */
+ 0x04b6, 501, /* Ҷ ҷ */
+ 0x04b8, 501, /* Ҹ ҹ */
+ 0x04ba, 501, /* Һ һ */
+ 0x04bc, 501, /* Ҽ ҽ */
+ 0x04be, 501, /* Ҿ ҿ */
+ 0x04c1, 501, /* Ӂ ӂ */
+ 0x04c3, 501, /* Ӄ ӄ */
+ 0x04c7, 501, /* Ӈ ӈ */
+ 0x04cb, 501, /* Ӌ ӌ */
+ 0x04d0, 501, /* Ӑ ӑ */
+ 0x04d2, 501, /* Ӓ ӓ */
+ 0x04d4, 501, /* Ӕ ӕ */
+ 0x04d6, 501, /* Ӗ ӗ */
+ 0x04d8, 501, /* Ә ә */
+ 0x04da, 501, /* Ӛ ӛ */
+ 0x04dc, 501, /* Ӝ ӝ */
+ 0x04de, 501, /* Ӟ ӟ */
+ 0x04e0, 501, /* Ӡ ӡ */
+ 0x04e2, 501, /* Ӣ ӣ */
+ 0x04e4, 501, /* Ӥ ӥ */
+ 0x04e6, 501, /* Ӧ ӧ */
+ 0x04e8, 501, /* Ө ө */
+ 0x04ea, 501, /* Ӫ ӫ */
+ 0x04ee, 501, /* Ӯ ӯ */
+ 0x04f0, 501, /* Ӱ ӱ */
+ 0x04f2, 501, /* Ӳ ӳ */
+ 0x04f4, 501, /* Ӵ ӵ */
+ 0x04f8, 501, /* Ӹ ӹ */
+ 0x1e00, 501, /* Ḁ ḁ */
+ 0x1e02, 501, /* Ḃ ḃ */
+ 0x1e04, 501, /* Ḅ ḅ */
+ 0x1e06, 501, /* Ḇ ḇ */
+ 0x1e08, 501, /* Ḉ ḉ */
+ 0x1e0a, 501, /* Ḋ ḋ */
+ 0x1e0c, 501, /* Ḍ ḍ */
+ 0x1e0e, 501, /* Ḏ ḏ */
+ 0x1e10, 501, /* Ḑ ḑ */
+ 0x1e12, 501, /* Ḓ ḓ */
+ 0x1e14, 501, /* Ḕ ḕ */
+ 0x1e16, 501, /* Ḗ ḗ */
+ 0x1e18, 501, /* Ḙ ḙ */
+ 0x1e1a, 501, /* Ḛ ḛ */
+ 0x1e1c, 501, /* Ḝ ḝ */
+ 0x1e1e, 501, /* Ḟ ḟ */
+ 0x1e20, 501, /* Ḡ ḡ */
+ 0x1e22, 501, /* Ḣ ḣ */
+ 0x1e24, 501, /* Ḥ ḥ */
+ 0x1e26, 501, /* Ḧ ḧ */
+ 0x1e28, 501, /* Ḩ ḩ */
+ 0x1e2a, 501, /* Ḫ ḫ */
+ 0x1e2c, 501, /* Ḭ ḭ */
+ 0x1e2e, 501, /* Ḯ ḯ */
+ 0x1e30, 501, /* Ḱ ḱ */
+ 0x1e32, 501, /* Ḳ ḳ */
+ 0x1e34, 501, /* Ḵ ḵ */
+ 0x1e36, 501, /* Ḷ ḷ */
+ 0x1e38, 501, /* Ḹ ḹ */
+ 0x1e3a, 501, /* Ḻ ḻ */
+ 0x1e3c, 501, /* Ḽ ḽ */
+ 0x1e3e, 501, /* Ḿ ḿ */
+ 0x1e40, 501, /* Ṁ ṁ */
+ 0x1e42, 501, /* Ṃ ṃ */
+ 0x1e44, 501, /* Ṅ ṅ */
+ 0x1e46, 501, /* Ṇ ṇ */
+ 0x1e48, 501, /* Ṉ ṉ */
+ 0x1e4a, 501, /* Ṋ ṋ */
+ 0x1e4c, 501, /* Ṍ ṍ */
+ 0x1e4e, 501, /* Ṏ ṏ */
+ 0x1e50, 501, /* Ṑ ṑ */
+ 0x1e52, 501, /* Ṓ ṓ */
+ 0x1e54, 501, /* Ṕ ṕ */
+ 0x1e56, 501, /* Ṗ ṗ */
+ 0x1e58, 501, /* Ṙ ṙ */
+ 0x1e5a, 501, /* Ṛ ṛ */
+ 0x1e5c, 501, /* Ṝ ṝ */
+ 0x1e5e, 501, /* Ṟ ṟ */
+ 0x1e60, 501, /* Ṡ ṡ */
+ 0x1e62, 501, /* Ṣ ṣ */
+ 0x1e64, 501, /* Ṥ ṥ */
+ 0x1e66, 501, /* Ṧ ṧ */
+ 0x1e68, 501, /* Ṩ ṩ */
+ 0x1e6a, 501, /* Ṫ ṫ */
+ 0x1e6c, 501, /* Ṭ ṭ */
+ 0x1e6e, 501, /* Ṯ ṯ */
+ 0x1e70, 501, /* Ṱ ṱ */
+ 0x1e72, 501, /* Ṳ ṳ */
+ 0x1e74, 501, /* Ṵ ṵ */
+ 0x1e76, 501, /* Ṷ ṷ */
+ 0x1e78, 501, /* Ṹ ṹ */
+ 0x1e7a, 501, /* Ṻ ṻ */
+ 0x1e7c, 501, /* Ṽ ṽ */
+ 0x1e7e, 501, /* Ṿ ṿ */
+ 0x1e80, 501, /* Ẁ ẁ */
+ 0x1e82, 501, /* Ẃ ẃ */
+ 0x1e84, 501, /* Ẅ ẅ */
+ 0x1e86, 501, /* Ẇ ẇ */
+ 0x1e88, 501, /* Ẉ ẉ */
+ 0x1e8a, 501, /* Ẋ ẋ */
+ 0x1e8c, 501, /* Ẍ ẍ */
+ 0x1e8e, 501, /* Ẏ ẏ */
+ 0x1e90, 501, /* Ẑ ẑ */
+ 0x1e92, 501, /* Ẓ ẓ */
+ 0x1e94, 501, /* Ẕ ẕ */
+ 0x1ea0, 501, /* Ạ ạ */
+ 0x1ea2, 501, /* Ả ả */
+ 0x1ea4, 501, /* Ấ ấ */
+ 0x1ea6, 501, /* Ầ ầ */
+ 0x1ea8, 501, /* Ẩ ẩ */
+ 0x1eaa, 501, /* Ẫ ẫ */
+ 0x1eac, 501, /* Ậ ậ */
+ 0x1eae, 501, /* Ắ ắ */
+ 0x1eb0, 501, /* Ằ ằ */
+ 0x1eb2, 501, /* Ẳ ẳ */
+ 0x1eb4, 501, /* Ẵ ẵ */
+ 0x1eb6, 501, /* Ặ ặ */
+ 0x1eb8, 501, /* Ẹ ẹ */
+ 0x1eba, 501, /* Ẻ ẻ */
+ 0x1ebc, 501, /* Ẽ ẽ */
+ 0x1ebe, 501, /* Ế ế */
+ 0x1ec0, 501, /* Ề ề */
+ 0x1ec2, 501, /* Ể ể */
+ 0x1ec4, 501, /* Ễ ễ */
+ 0x1ec6, 501, /* Ệ ệ */
+ 0x1ec8, 501, /* Ỉ ỉ */
+ 0x1eca, 501, /* Ị ị */
+ 0x1ecc, 501, /* Ọ ọ */
+ 0x1ece, 501, /* Ỏ ỏ */
+ 0x1ed0, 501, /* Ố ố */
+ 0x1ed2, 501, /* Ồ ồ */
+ 0x1ed4, 501, /* Ổ ổ */
+ 0x1ed6, 501, /* Ỗ ỗ */
+ 0x1ed8, 501, /* Ộ ộ */
+ 0x1eda, 501, /* Ớ ớ */
+ 0x1edc, 501, /* Ờ ờ */
+ 0x1ede, 501, /* Ở ở */
+ 0x1ee0, 501, /* Ỡ ỡ */
+ 0x1ee2, 501, /* Ợ ợ */
+ 0x1ee4, 501, /* Ụ ụ */
+ 0x1ee6, 501, /* Ủ ủ */
+ 0x1ee8, 501, /* Ứ ứ */
+ 0x1eea, 501, /* Ừ ừ */
+ 0x1eec, 501, /* Ử ử */
+ 0x1eee, 501, /* Ữ ữ */
+ 0x1ef0, 501, /* Ự ự */
+ 0x1ef2, 501, /* Ỳ ỳ */
+ 0x1ef4, 501, /* Ỵ ỵ */
+ 0x1ef6, 501, /* Ỷ ỷ */
+ 0x1ef8, 501, /* Ỹ ỹ */
+ 0x1f59, 492, /* Ὑ ὑ */
+ 0x1f5b, 492, /* Ὓ ὓ */
+ 0x1f5d, 492, /* Ὕ ὕ */
+ 0x1f5f, 492, /* Ὗ ὗ */
+ 0x1fbc, 491, /* ᾼ ᾳ */
+ 0x1fcc, 491, /* ῌ ῃ */
+ 0x1fec, 493, /* Ῥ ῥ */
+ 0x1ffc, 491 /* ῼ ῳ */
+]
+
+/*
+ * title characters are those between
+ * upper and lower case. ie DZ Dz dz
+ */
+const rtotitle1 = [
+ 0x01c4, 501, /* DŽ Dž */
+ 0x01c6, 499, /* dž Dž */
+ 0x01c7, 501, /* LJ Lj */
+ 0x01c9, 499, /* lj Lj */
+ 0x01ca, 501, /* NJ Nj */
+ 0x01cc, 499, /* nj Nj */
+ 0x01f1, 501, /* DZ Dz */
+ 0x01f3, 499 /* dz Dz */
+]
+
+const findc = {c, t, sz, nelt, ret
+ var l
+ var m
+
+ /* we're processing in chunks of size
+ nelt, so 1 chunk is of length 'nelt' */
+ while t.len > nelt
+ sz /= 2
+ m = sz*nelt
+ l = t[m:]
+ if c >= l[0]
+ t = l[0:m]
+ else
+ t = t[0:m]
+ ;;
+ ;;
+
+ if t.len != 0 && c >= t[0]
+ ret# = t
+ -> true
+ else
+ -> false
+ ;;
+}
+
+
+const isalpha = {c
+ var l
+
+ if isupper(c) || islower(c)
+ -> true
+ elif findc(c, ralpha2[:], ralpha2.len/2, 2, &l)
+ if (c >= l[0] && c <= l[1])
+ -> true
+ ;;
+ elif findc(c, ralpha1[:], ralpha1.len, 1, &l)
+ if (c == l[0])
+ -> true
+ ;;
+ ;;
+ -> false
+}
+
+const isnum = {c
+ var l
+
+ if findc(c, rnums[:], rnums.len/2, 2, &l)
+ if(c >= l[0] && c <= l[1])
+ -> true
+ ;;
+ ;;
+ -> false
+}
+
+const isalnum = {c
+ -> isalpha(c) || isnum(c)
+}
+
+const isblank = {c
+ var l
+ var sl
+ var len
+
+ l = rspace2[:]
+ sl = rspace2[:]
+ len = rspace2.len/2
+ if findc(c, sl, len, 2, &l)
+ if(c >= l[0] && c <= l[1])
+ -> true
+ ;;
+ ;;
+ -> false
+}
+
+const isspace = {c
+ -> c == '\n' || isblank(c)
+}
+
+const islower = {c
+ var l
+
+ if findc(c, rtoupper2[:], rtoupper2.len, 2, &l)
+ if (c >= l[0] && c <= l[1])
+ -> true
+ ;;
+ elif findc(c, rtoupper1[:], rtoupper1.len, 1, &l)
+ if (c == l[0])
+ -> true
+ ;;
+ ;;
+ -> false
+}
+
+const isupper = {c
+ var l
+
+ if findc(c, rtolower2[:], rtolower2.len, 2, &l)
+ if (c >= l[0] && c <= l[1])
+ -> true
+ ;;
+ elif findc(c, rtolower1[:], rtolower1.len, 1, &l)
+ if (c == l[0])
+ -> true
+ ;;
+ ;;
+ -> false
+}
+
+const istitle = {c
+ -> isupper(c) && islower(c)
+}
+
+const tolower = {c
+ var l
+
+ if findc(c, rtolower2[:], rtolower2.len/3, 3, &l)
+ if c >= l[0] && c <= l[1]
+ -> c + l[2] - 500;
+ ;;
+ elif findc(c, rtolower1[:], rtolower1.len/2, 2, &l)
+ if c == l[0]
+ -> c + l[1] - 500;
+ ;;
+ ;;
+ -> c
+}
+
+const toupper = {c
+ var l
+
+ if findc(c, rtoupper2[:], rtoupper2.len/3, 3, &l);
+ if c >= l[0] && c <= l[1]
+ -> c + l[2] - 500;
+ ;;
+ elif findc(c, rtoupper1[:], rtoupper1.len/2, 2, &l);
+ if c == l[0]
+ -> c + l[1] - 500;
+ ;;
+ ;;
+ -> c
+}
+
+const totitle = {c
+ var l
+
+ if findc(c, rtotitle1[:], rtotitle1.len/2, 2, &l);
+ if c == l[0]
+ -> c + l[1] - 500;
+ ;;
+ ;;
+ -> c
+}
+
+generic charval = {c, base -> @a::(numeric,integral)
+ var v = -1
+
+ if c >= '0' && c <= '9'
+ v = (c - '0') castto(@a::(integral,numeric))
+ elif c >= 'a' && c <= 'z'
+ v = (c - 'a' + 10) castto(@a::(integral,numeric))
+ elif c >= 'A' && c <= 'Z'
+ v = (c - 'A' + 10) castto(@a::(integral,numeric))
+ ;;
+
+ if v < 0 || v > (base castto(@a::(integral,numeric)))
+ -> -1
+ ;;
+ -> v
+}
--- /dev/null
+++ b/libstd/cmp.myr
@@ -1,0 +1,54 @@
+use "extremum.use"
+use "types.use"
+
+pkg std =
+ type order = union
+ `Before
+ `Equal
+ `After
+ ;;
+
+ generic numcmp : (a : @a, b : @a -> order)
+ const strcmp : (a : byte[:], b : byte[:] -> order)
+ const strncmp : (a : byte[:], b : byte[:], n : size -> order)
+;;
+
+generic numcmp = {a, b
+ if a < b
+ -> `Before
+ elif a == b
+ -> `Equal
+ else
+ -> `After
+ ;;
+}
+
+const strcmp = {a, b
+ var l
+ var i
+
+ l = min(a.len, b.len)
+ for i = 0; i < l; i++
+ if a[i] < b[i]
+ -> `Before
+ elif a[i] > b[i]
+ -> `After
+ ;;
+ ;;
+
+ if a.len < b.len
+ -> `Before
+ elif a.len > b.len
+ -> `After
+ else
+ -> `Equal
+ ;;
+
+}
+
+const strncmp = {a, b, n
+ a = a[:min(a.len, n)]
+ b = b[:min(b.len, n)]
+ -> strcmp(a, b)
+}
+
--- /dev/null
+++ b/libstd/dial.myr
@@ -1,0 +1,131 @@
+use "alloc.use"
+use "chartype.use"
+use "die.use"
+use "result.use"
+use "sys.use"
+use "sleq.use"
+use "option.use"
+use "ipparse.use"
+use "resolve.use"
+use "fmt.use"
+use "endian.use"
+use "intparse.use"
+use "hasprefix.use"
+use "utf.use"
+
+pkg std =
+ const dial : (dialstr : byte[:] -> result(fd, byte[:]))
+;;
+
+/*
+ a map from service name to a list of (port,proto)
+ pairs in order of preference
+*/
+/* FIXME: implement
+var services : htab(byte[:], [int, byte[:]][:])#
+var inited = false
+*/
+
+/* takes a plan 9 style dial string */
+const dial = {str
+ var proto, host, port
+ var socktype, portnum
+ var sa : sockaddr_in /* we only support inet sockets right now.. ugh. */
+ var sock
+
+ (proto, str) = nameseg(str)
+ (host, str) = nameseg(str)
+ (port, str) = nameseg(str)
+
+ if proto.len == 0
+ -> `Fail "missing proto"
+ elif host.len == 0
+ -> `Fail "missing host"
+ elif port.len == 0
+ -> `Fail "missing port"
+ ;;
+
+ if sleq(proto, "net")
+ -> `Fail "net wildcard proto not yet supported\n"
+ elif sleq(proto, "unix")
+ -> `Fail "net unix proto not yet supported\n"
+ elif sleq(proto, "tcp")
+ socktype = Sockstream
+ elif sleq(proto, "udp")
+ socktype = Sockdgram
+ ;;
+
+ match parseport(port)
+ | `Some n: portnum = n
+ | `None: -> `Fail "bad port"
+ ;;
+
+ match getaddr(host)
+ | `Ipv4 bits:
+ sa.fam = Afinet
+ sa.addr = bits
+ sa.port = hosttonet(portnum)
+ | `Ipv6 bits:
+ -> `Fail "ipv6 not yet supported"
+ ;;
+
+ sock = socket(sa.fam, socktype, 0)
+ if sock < 0
+ -> `Fail "failed to connect to socket"
+ ;;
+ var err
+ err = connect(sock, (&sa) castto(sockaddr#), sizeof(sockaddr_in))
+ if err < 0
+ put("Errno %i\n", -err)
+ close(sock)
+ -> `Fail "Failed to bind socket"
+ ;;
+
+ -> `Ok sock
+}
+
+const parseport = {port
+ match intparse(port)
+ | `Some n: -> `Some n
+ | `None:
+ /* a small number of hardcoded ports */
+ if sleq(port, "http")
+ -> `Some 80
+ elif sleq(port, "https")
+ -> `Some 443
+ elif sleq(port, "ircd")
+ -> `Some 6667
+ elif sleq(port, "dns")
+ -> `Some 53
+ ;;
+ ;;
+ -> `None
+}
+
+const getaddr = {addr
+ var ip
+
+ match ipparse(addr)
+ | `Some a: ip = a
+ | `None:
+ match resolve(addr)
+ | `Ok hi:
+ ip = hi[0].addr
+ slfree(hi)
+ | `Fail m:
+ ;;
+ ;;
+ -> ip
+}
+
+const nameseg = {str
+ var len
+
+ for len = 0; len < str.len; len++
+ if str[len] == '!' castto(byte)
+ -> (str[:len], str[len+1:])
+ ;;
+ ;;
+ -> (str[:], str[len:])
+}
+
--- /dev/null
+++ b/libstd/die.myr
@@ -1,0 +1,19 @@
+use "sys.use"
+use "types.use"
+
+pkg std =
+ const die : (msg : byte[:] -> void)
+ const assert : (cond : bool, msg : byte[:] -> void)
+;;
+
+const die = {msg
+ write(2, msg)
+ kill(getpid(), 6)
+}
+
+const assert = {cond, msg
+ if !cond
+ die(msg)
+ ;;
+}
+
--- /dev/null
+++ b/libstd/endian.myr
@@ -1,0 +1,32 @@
+pkg std =
+ generic hosttonet : (v : @a -> @a)
+ generic nettohost : (v : @a -> @a)
+;;
+
+/* FIXME: we only support little endian platforms right now,
+ so we assume a little endian machine. FIX THIS. */
+generic hosttonet = {v : @a::(integral,numeric)
+ var i
+ var ret
+
+ ret = 0
+ for i = 0; i < sizeof(@a); i++
+ ret <<= 8
+ ret |= v & 0xff
+ v >>= 8
+ ;;
+ -> ret
+}
+
+generic nettohost = {v : @a::(integral,numeric)
+ var i
+ var ret
+
+ ret = 0
+ for i = 0; i < sizeof(@a); i++
+ ret <<= 8
+ ret |= v & 0xff
+ v >>= 8
+ ;;
+ -> ret
+}
--- /dev/null
+++ b/libstd/env.myr
@@ -1,0 +1,21 @@
+use "extremum.use"
+use "option.use"
+use "sleq.use"
+
+pkg std =
+ extern var _environment : byte[:][:]
+
+ const getenv : (name : byte[:] -> std.option(byte[:]))
+;;
+
+const getenv = {name
+ var n
+ for env in _environment
+ n = min(name.len, env.len)
+ if sleq(name, env[:n]) && sleq(env[n:n+1], "=")
+ -> `Some env[n+1:]
+ ;;
+ ;;
+ -> `None
+}
+
--- /dev/null
+++ b/libstd/execvp.myr
@@ -1,0 +1,57 @@
+use "alloc.use"
+use "env.use"
+use "fmt.use"
+use "option.use"
+use "strfind.use"
+use "strsplit.use"
+use "sys.use"
+
+pkg std =
+ const execvp : (cmd : byte[:], args : byte[:][:] -> int64)
+ const execvpe : (cmd : byte[:], args : byte[:][:], env : byte[:][:] -> int64)
+;;
+
+const execvp = {cmd, args
+ var paths, cmdlen
+ var buf : byte[512]
+
+ match strfind(cmd, "/")
+ | `Some _:
+ -> execv(cmd, args)
+ | `None:
+ paths = getpaths()
+ for p in paths
+ cmdlen = bfmt(buf[:], "%s/%s", p, cmd)
+ execv(buf[:cmdlen], args)
+ ;;
+ slfree(paths)
+ ;;
+ -> -1
+}
+
+const execvpe = {cmd, args, env
+ var paths, cmdlen
+ var buf : byte[512]
+
+ match strfind(cmd, "/")
+ | `Some _:
+ -> execve(cmd, args, env)
+ | `None:
+ paths = getpaths()
+ for p in paths
+ cmdlen = bfmt(buf[:], "%s/%s", p, cmd)
+ execve(buf[:cmdlen], args, env)
+ ;;
+ slfree(paths)
+ ;;
+ -> -1
+}
+
+const getpaths = {
+ var path
+ match getenv("PATH")
+ | `Some p: path = p
+ | `None: path = "/usr/local/bin:/bin:/usr/bin"
+ ;;
+ -> strsplit(path, ":")
+}
--- /dev/null
+++ b/libstd/extremum.myr
@@ -1,0 +1,20 @@
+pkg std =
+ generic min : (a : @a::numeric, b : @a::numeric -> @a::numeric)
+ generic max : (a : @a::numeric, b : @a::numeric -> @a::numeric)
+;;
+
+generic min = {a, b
+ if a < b
+ -> a
+ else
+ -> b
+ ;;
+}
+
+generic max = {a, b
+ if a > b
+ -> a
+ else
+ -> b
+ ;;
+}
--- /dev/null
+++ b/libstd/floatbits.myr
@@ -1,0 +1,60 @@
+pkg std =
+ const float64bits : (flt : float64 -> uint64)
+ const float32bits : (flt : float32 -> uint32)
+ const float64frombits : (bits : uint64 -> float64)
+ const float32frombits : (bits : uint32 -> float32)
+ const float64explode : (flt : float64 -> [bool, uint64, int32])
+ const float32explode : (flt : float64 -> [bool, uint64, int32])
+;;
+
+const float64bits = {flt; -> (&flt castto(uint64#))#}
+const float32bits = {flt; -> (&flt castto(uint32#))#}
+const float64frombits = {bits; -> (&bits castto(float64#))#}
+const float32frombits = {bits; -> (&bits castto(float32#))#}
+
+const float64explode = {flt
+ var bits, isneg, mant, exp
+
+ bits = float64bits(flt)
+ isneg = (bits >> 63) == 0 /* msb is sign bit */
+ exp = (bits >> 52) & 0x7ff /* exp is in bits [52..63] */
+ mant = bits & ((1ul << 52) - 1) /* msb is in bits [..51] */
+
+ /* add back the implicit bit if this is not a denormal */
+ if exp != 0
+ mant |= 1ul << 52
+ else
+ exp = 1
+ ;;
+ /*
+ adjust for exponent bias. nb: because we are
+ treating the mantissa as m.0 instead of 0.m,
+ our exponent bias needs to be offset by the
+ size of m
+ */
+ -> (isneg, mant, (exp castto(int32)) - 1075)
+}
+
+const float32explode = {flt
+ var bits, isneg, mant, exp
+
+ bits = float64bits(flt) castto(uint64)
+ isneg = (bits >> 31) == 0 /* msb is sign bit */
+ exp = (bits >> 22) & 0xff /* exp is in bits [23..30] */
+ mant = bits & ((1ul << 52) - 1) /* msb is in bits [0..22] */
+
+ /* add back the implicit bit if this is not a denormal */
+ if exp != 0
+ mant |= 1ul << 22
+ else
+ exp = 1
+ ;;
+ /*
+ adjust for exponent bias. nb: because we are
+ treating the mantissa as m.0 instead of 0.m,
+ our exponent bias needs to be offset by the
+ size of m
+ */
+ -> (isneg, mant, (exp castto(int32)) - 149)
+}
+
--- /dev/null
+++ b/libstd/fmt.myr
@@ -1,0 +1,282 @@
+use "alloc.use"
+use "die.use"
+use "sys.use"
+use "types.use"
+use "utf.use"
+use "varargs.use"
+use "extremum.use"
+
+/*
+ printf-like functions. These use a different syntax from the C printf,
+ as described below:
+
+ %s - A string, ie, a utf8 encoded byte slice.
+ %t - A boolean
+ %b - A byte.
+ %w - A 16 bit integer
+ %i - A 32 bit integer
+ %l - A 64 bit integer
+ %z - A size
+ %p - A pointer
+ %c - A char
+*/
+
+pkg std =
+ const put : (fmt : byte[:], args : ... -> size)
+ const putv : (fmt : byte[:], ap : valist -> size)
+ const fatal : (status : int, fmt : byte[:], args : ... -> void)
+ const fatalv : (status : int, fmt : byte[:], ap : valist -> void)
+ const fmt : (fmt : byte[:], args : ... -> byte[:])
+ const fmtv : (fmt : byte[:], ap : valist -> byte[:])
+ const bfmt : (buf : byte[:], fmt : byte[:], args : ... -> size)
+ const bfmtv : (buf : byte[:], fmt : byte[:], ap : valist -> size)
+;;
+
+/* Writes a string of text up to 2 kb in size to stdout */
+const put = {fmt, args
+ -> putv(fmt, vastart(&args))
+}
+
+/* Writes a string of text up to 2kb long to stdout, using a valist
+ as the source of the arguments */
+const putv = {fmt, ap
+ var buf : byte[2048]
+ var n
+
+ n = bfmtv(buf[:], fmt, ap)
+ write(1, buf[:n])
+ -> n
+}
+
+/* same as 'put', but exits the program after printing */
+const fatal = {status, fmt, args
+ putv(fmt, vastart(&args))
+ exit(status)
+}
+
+/* same as 'putv', but exits the program after printing */
+const fatalv = {status, fmt, ap
+ putv(fmt, ap)
+ exit(status)
+}
+
+/* formats a string, allocating the slice. FIXME: calculate the
+ size needed. */
+const fmt = {fmt, args
+ -> fmtv(fmt, vastart(&args))
+}
+
+/* formats a string, allocating the slice. FIXME: calculate the
+ size needed. Takes a valist as it's last argument. */
+const fmtv = {fmt, ap
+ var buf
+ var sz
+
+ buf = slalloc(2048)
+ sz = bfmtv(buf, fmt, ap)
+ -> buf[:sz]
+}
+
+/* formats a string of text as specified by 'fmt' into 'buf' */
+const bfmt = {buf, fmt, args
+ -> bfmtv(buf, fmt, vastart(&args))
+}
+
+const digitchars = [
+ '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
+]
+generic intfmt = {buf : byte[:], bits : @a::(integral,numeric), base, signed
+ var isneg
+ var val
+ var b : char[32]
+ var i
+ var j
+ var n
+
+ n = 0
+ i = 0
+ if signed && bits < 0
+ val = -bits castto(uint64)
+ isneg = true
+ else
+ val = bits castto(uint64)
+ val &= ~0 >> (8*(sizeof(uint64)-sizeof(@a)))
+ isneg = false
+ ;;
+
+ if val == 0
+ b[0] = '0'
+ i++
+ ;;
+ while val != 0
+ b[i] = digitchars[val % base]
+ val /= base
+ i++
+ ;;
+ n = 0
+ if isneg
+ n += encode(buf[n:], '-')
+ ;;
+ for j = i; j != 0; j--
+ n += encode(buf[n:], b[j - 1])
+ ;;
+ -> n
+}
+
+/* formats a string of text as specified by 'fmt' into 'buf',
+ using a valist for the arguments */
+const bfmtv = {buf, fmt, ap
+ var c
+ var n
+ var base
+ var signed
+ var s_val : byte[:]
+ var t_val : bool
+ var b_val : int8, ub_val : uint8
+ var w_val : int16, uw_val : uint16
+ var i_val : int32, ui_val : uint32
+ var l_val : int64, ul_val : uint64
+ var z_val : size
+ var p_val : byte#
+ var c_val : char
+ var f_val : float64, F_val : float32
+
+ n = 0
+ while fmt.len != 0
+ (c, fmt) = striter(fmt)
+ if c == '%'
+ base = 10
+ signed = true
+ (c, fmt) = striter(fmt)
+ /* modifiers */
+ if fmt.len > 0
+ match c
+ | 'x':
+ (c, fmt) = striter(fmt)
+ base = 16
+ signed = false
+
+ | 'u':
+ (c, fmt) = striter(fmt)
+ signed = false
+ ;;
+ ;;
+ /* format specifiers */
+ match c
+ | 's':
+ (s_val, ap) = vanext(ap)
+ n += strfmt(buf[n:], s_val)
+ | 't':
+ (t_val, ap) = vanext(ap)
+ n += boolfmt(buf[n:], t_val)
+ | 'f':
+ (f_val, ap) = vanext(ap)
+ n += floatfmt(buf[n:], f_val, 0, 0)
+ /* FIXME: float casts are currently broken
+ | 'F':
+ (F_val, ap) = vanext(ap)
+ n += floatfmt(buf[n:], F_val castto(float64))
+ */
+ /* format integers */
+ | 'b':
+ if signed
+ (b_val, ap) = vanext(ap)
+ n += intfmt(buf[n:], b_val, base, signed)
+ else
+ (ub_val, ap) = vanext(ap)
+ n += intfmt(buf[n:], ub_val, base, signed)
+ ;;
+ | 'w':
+ if signed
+ (w_val, ap) = vanext(ap)
+ n += intfmt(buf[n:], w_val, base, signed)
+ else
+ (uw_val, ap) = vanext(ap)
+ n += intfmt(buf[n:], uw_val, base, signed)
+ ;;
+ | 'i':
+ if signed
+ (i_val, ap) = vanext(ap)
+ n += intfmt(buf[n:], i_val, base, signed)
+ else
+ (ui_val, ap) = vanext(ap)
+ n += intfmt(buf[n:], ui_val, base, signed)
+ ;;
+ | 'l':
+ if signed
+ (l_val, ap) = vanext(ap)
+ n += intfmt(buf[n:], l_val, base, signed)
+ else
+ (ul_val, ap) = vanext(ap)
+ n += intfmt(buf[n:], ul_val, base, signed)
+ ;;
+
+ | 'z':
+ (z_val, ap) = vanext(ap)
+ n += intfmt(buf[n:], z_val castto(int64), base, signed)
+ | 'p':
+ (p_val, ap) = vanext(ap)
+ n += intfmt(buf[n:], p_val castto(int64), 16, false)
+ | 'c': (c_val, ap) = vanext(ap)
+ n += encode(buf[n:], c_val)
+ | _:
+ die("Unknown format specifier\n")
+ ;;
+ else
+ n += encode(buf[n:], c)
+ ;;
+ ;;
+ -> n
+}
+
+const strfmt = {buf, str
+ var i
+
+ for i = 0; i < min(str.len, buf.len); i++
+ buf[i] = str[i]
+ ;;
+ -> i
+}
+
+const boolfmt = {buf, val
+ var s
+
+ if val
+ s = "true"
+ else
+ s = "false"
+ ;;
+ -> strfmt(buf, s)
+}
+
+/*
+ buf: the output buffer.
+ val: the value to format, in float64 format.
+ mode: the truncation mode.
+ 0 => print until precision exhausted.
+ 1 => print until precision exhausted or maxdigits produced.
+ 2 => print until maxdigits produced, paddding with zeros.
+ */
+
+const floatfmt = {buf, val, mode, maxdigits
+ var i
+ var n
+ /*
+ var isneg
+ */
+ /*var b, e, f*/
+
+ /* handle 0 specially to avoid special cases */
+ if val == 0.0
+ n = strfmt(buf, "0.0")
+ if mode == 0 && maxdigits > 2
+ for i = 1; i < maxdigits; i++
+ n += strfmt(buf[n:], "0")
+ ;;
+ ;;
+ -> n
+ ;;
+
+ -> strfmt(buf, "floats not implemented")
+}
+
--- /dev/null
+++ b/libstd/hashfuncs.myr
@@ -1,0 +1,89 @@
+use "sleq.use"
+use "types.use"
+
+pkg std =
+ const strhash : (s : byte[:] -> uint32)
+ const streq : (a : byte[:], b : byte[:] -> bool)
+
+ generic ptrhash : (p : @a# -> uint32)
+ generic ptreq : (a : @a#, b : @a# -> bool)
+
+ generic inthash : (v : @a::(integral,numeric) -> uint32)
+ generic inteq : (a : @a::(integral,numeric), b : @a::(integral,numeric) -> bool)
+
+ const murmurhash2 : (data : byte[:], seed : uint32 -> uint32)
+;;
+
+const Seed = 1234
+
+/* Supremely simple djb hash. */
+const strhash = {s
+ -> murmurhash2(s, Seed)
+}
+
+const streq = {a, b
+ -> sleq(a, b)
+}
+
+generic ptrhash = {p : @a#
+ var x
+
+ x = &p castto(byte#)
+ -> murmurhash2(x[0:sizeof(@a)], Seed)
+}
+
+generic ptreq = {a, b
+ -> a == b
+}
+
+generic inthash = {v : @a::(integral,numeric)
+ var p
+
+ p = &v castto(byte#)
+ -> murmurhash2(p[0:sizeof(@a)], Seed)
+}
+
+generic inteq = {a, b
+ -> a == b
+}
+
+const murmurhash2 = {data, seed
+ const m = 0x5bd1e995;
+ const r = 24
+ var h, k
+
+ h = seed ^ data.len
+ while data.len >= 4
+ k = (data[0] castto(uint32))
+ k |= (data[1] castto(uint32)) << 8
+ k |= (data[2] castto(uint32)) << 16
+ k |= (data[3] castto(uint32)) << 24
+
+ k *= m
+ k ^= k >> r
+ k *= m
+
+ h *= m
+ h ^= k
+ data = data[4:]
+ ;;
+
+ match data.len
+ | 3:
+ h ^= (data[2] castto(uint32)) << 16
+ h ^= (data[1] castto(uint32)) <<8
+ h ^= (data[0] castto(uint32))
+ | 2:
+ h ^= (data[1] castto(uint32)) <<8
+ h ^= (data[0] castto(uint32))
+ | 1:
+ h ^= (data[0] castto(uint32))
+ ;;
+ h *= m
+
+ h ^= h >> 13
+ h *= m
+ h ^= h >> 15
+
+ -> h
+}
--- /dev/null
+++ b/libstd/hasprefix.myr
@@ -1,0 +1,12 @@
+use "cmp.use"
+pkg std =
+ const hasprefix : (s : byte[:], pre : byte[:] -> bool)
+;;
+
+const hasprefix = {s, pre
+ match strncmp(s, pre, pre.len)
+ | `Equal: -> true
+ | _: -> false
+ ;;
+}
+
--- /dev/null
+++ b/libstd/hassuffix.myr
@@ -1,0 +1,17 @@
+use "cmp.use"
+pkg std =
+ const hassuffix : (s : byte[:], suff : byte[:] -> bool)
+;;
+
+const hassuffix = {s, suff
+ var tail
+
+ if suff.len >= s.len
+ tail = s[s.len - suff.len:]
+ match strncmp(tail, suff, suff.len)
+ | `Equal: -> true
+ | _: -> false
+ ;;
+ ;;
+}
+
--- /dev/null
+++ b/libstd/htab.myr
@@ -1,0 +1,199 @@
+use "alloc.use"
+use "die.use"
+use "extremum.use"
+use "fmt.use"
+use "option.use"
+use "types.use"
+
+pkg std =
+ type htab(@k, @v) = struct
+ hash : (k : @k -> uint32)
+ eq : (a : @k, b : @k -> bool)
+
+ nelt : size
+ keys : @k[:]
+ vals : @v[:]
+ hashes : uint32[:]
+ dead : bool[:]
+ ;;
+
+ generic mkht : (h : (k : @k -> uint32), eq : (a : @k, b : @k -> bool) -> htab(@k, @v)#)
+ generic htfree : (ht : htab(@k, @v)# -> void)
+ generic htput : (ht : htab(@k, @v)#, k : @k, v : @v -> void)
+ generic htdel : (ht : htab(@k, @v)#, k : @k -> void)
+ generic htget : (ht : htab(@k, @v)#, k : @k -> option(@v))
+ generic htgetv : (ht : htab(@k, @v)#, k : @k, fallback : @v-> @v)
+ generic hthas : (ht : htab(@k, @v)#, k : @k -> bool)
+ generic htkeys : (ht : htab(@k, @v)# -> @k[:])
+;;
+
+const Initsz = 32
+
+generic hash = {ht, k
+ var h
+
+ h = ht.hash(k)
+ if h == 0
+ -> 1
+ else
+ -> h
+ ;;
+}
+
+generic resize = {ht
+ var oldk
+ var oldv
+ var oldh
+ var oldd
+ var sz
+ var i
+
+ oldk = ht.keys
+ oldv = ht.vals
+ oldh = ht.hashes
+ oldd = ht.dead
+ sz = 2*max(ht.keys.len, 1)
+ ht.keys = slalloc(sz)
+ ht.vals = slalloc(sz)
+ ht.hashes = slzalloc(sz)
+ ht.dead = slzalloc(sz)
+
+ ht.nelt = 0
+ for i = 0; i < oldk.len; i++
+ if oldh[i] != 0 && !oldd[i]
+ htput(ht, oldk[i], oldv[i])
+ ;;
+ ;;
+ slfree(oldk)
+ slfree(oldv)
+ slfree(oldh)
+ slfree(oldd)
+}
+
+generic idx = {ht, k
+ var i, di
+ var h
+
+ di = 0
+ h = hash(ht, k)
+ i = h & (ht.keys.len - 1)
+ while true
+ while ht.hashes[i] != 0 && !ht.dead[i] && ht.hashes[i] != h
+ di++
+ i = (h + di) & (ht.keys.len - 1)
+ ;;
+
+ if ht.hashes[i] == 0 || ht.dead[i]
+ -> `None
+ ;;
+ if ht.eq(ht.keys[i], k)
+ -> `Some i
+ ;;
+ ;;
+}
+
+generic mkht = {h, eq
+ var ht
+
+ ht = alloc()
+
+ ht.hash = h
+ ht.eq = eq
+
+ ht.nelt = 0
+ ht.keys = slalloc(Initsz)
+ ht.vals = slalloc(Initsz)
+ ht.hashes = slzalloc(Initsz)
+ ht.dead = slzalloc(Initsz)
+ -> ht
+}
+
+generic htfree = {ht
+ slfree(ht.keys)
+ slfree(ht.vals)
+ slfree(ht.hashes)
+ slfree(ht.dead)
+ free(ht)
+}
+
+generic htput = {ht, k, v
+ var i, di
+ var h
+ var done
+
+ di = 0
+ h = hash(ht, k)
+ i = h & (ht.keys.len - 1)
+ done = false
+ while ht.hashes[i] != 0 && !ht.dead[i] && !done
+ /*
+ second insertion just overwrites first.
+ nb: comparing keys for dead values is bad.
+ */
+ if ht.hashes[i] == h && (ht.dead[i] || ht.eq(ht.keys[i], k))
+ done = true
+ else
+ di++
+ i = (h + di) & (ht.keys.len - 1)
+ ;;
+ ;;
+ ht.hashes[i] = h
+ ht.keys[i] = k
+ ht.vals[i] = v
+ ht.dead[i] = false
+ ht.nelt++
+ if ht.keys.len < ht.nelt * 2
+ resize(ht)
+ ;;
+}
+
+generic htdel = {ht, k
+ match idx(ht, k)
+ | `Some i:
+ ht.dead[i] = true
+ ht.nelt--
+ /* remove tombstones if we shrink enough */
+ if ht.keys.len > ht.nelt * 4
+ resize(ht)
+ ;;
+ | _:
+ /* do nothing */
+ ;;
+}
+
+generic htget = {ht, k
+ match idx(ht, k)
+ | `Some i: -> `Some ht.vals[i]
+ | `None: -> `None
+ ;;
+}
+
+generic htgetv = {ht, k, v
+ match idx(ht, k)
+ | `Some i: -> ht.vals[i]
+ | `None: -> v
+ ;;
+}
+
+generic hthas = {ht, k
+ match idx(ht, k)
+ | `Some i: -> true
+ | `None: -> false
+ ;;
+}
+
+generic htkeys = {ht
+ var keys
+ var i
+ var j
+
+ keys = slalloc(ht.nelt)
+ j = 0
+ for i = 0; i < ht.keys.len; i++
+ if ht.hashes[i] != 0 && !ht.dead[i]
+ keys[j++] = ht.keys[i]
+ ;;
+ ;;
+ -> keys
+}
+
--- /dev/null
+++ b/libstd/ifreq-linux.myr
@@ -1,0 +1,67 @@
+use "sys.use"
+
+pkg std =
+ const Ifnamesz = 16
+
+ type ifreq_addr = struct
+ name : byte[Ifnamesz]
+ addr : sockaddr
+ ;;
+
+ type ifreq_dstaddr = struct
+ name : byte[Ifnamesz]
+ dstaddr : sockaddr
+ ;;
+
+ type ifreq_broadaddr = struct
+ name : byte[Ifnamesz]
+ broadaddr : sockaddr
+ ;;
+
+ type ifreq_netmask = struct
+ name : byte[Ifnamesz]
+ netmask : sockaddr
+ ;;
+
+
+ type ifreq_hwaddr = struct
+ name : byte[Ifnamesz]
+ hwaddr : sockaddr
+ ;;
+
+ type ifreq_flags = struct
+ name : byte[Ifnamesz]
+ flags : int16
+ ;;
+
+ type ifreq_ifindex = struct
+ name : byte[Ifnamesz]
+ index : int32
+ ;;
+
+ type ifreq_metric = struct
+ name : byte[Ifnamesz]
+ metric : int32
+ ;;
+
+
+ type ifreq_mtu = struct
+ name : byte[Ifnamesz]
+ mtu : int32
+ ;;
+
+ type ifreq_slave = struct
+ name : byte[Ifnamesz]
+ slave : byte[Ifnamesz]
+ ;;
+
+ type ifreq_newname = struct
+ name : byte[Ifnamesz]
+ newname : byte[Ifnamesz]
+ ;;
+
+ type ifreq_data = struct
+ name : byte[Ifnamesz]
+ data : void#
+ ;;
+;;
--- /dev/null
+++ b/libstd/ifreq-osx.myr
@@ -1,0 +1,77 @@
+use "sys.use"
+
+pkg std =
+ const Ifnamesz = 16
+
+ type ifreq_addr = struct
+ name : byte[Ifnamesz]
+ addr : sockaddr
+ ;;
+
+ type ifreq_dstaddr = struct
+ name : byte[Ifnamesz]
+ dstaddr : sockaddr
+ ;;
+
+ type ifreq_broadaddr = struct
+ name : byte[Ifnamesz]
+ broadaddr : sockaddr
+ ;;
+
+ type ifreq_flags = struct
+ name : byte[Ifnamesz]
+ flags : int16
+ ;;
+
+ type ifreq_metric = struct
+ name : byte[Ifnamesz]
+ metric : int32
+ ;;
+
+
+ type ifreq_phys = struct
+ name : byte[Ifnamesz]
+ phys : int32
+ ;;
+
+ type ifreq_media = struct
+ name : byte[Ifnamesz]
+ media : int32
+ ;;
+
+ type ifreq_data = struct
+ name : byte[Ifnamesz]
+ data : void#
+ ;;
+
+ type ifreq_devmtu = struct
+ name : byte[Ifnamesz]
+ cur : uint32
+ min : uint32
+ max : uint32
+ ;;
+
+ type ifreq_kpi = struct
+ name : byte[Ifnamesz]
+ modid : uint32
+ typeid : uint32
+ ptr : void#
+ ;;
+
+ type ifreq_wakeflg = struct
+ name : byte[Ifnamesz]
+ wakeflg : uint32
+ ;;
+
+ type ifreq_routerefs = struct
+ name : byte[Ifnamesz]
+ refs : uint32
+ ;;
+
+ type ifreq_icaps = struct
+ name : byte[Ifnamesz]
+ req : uint32
+ cur : uint32
+ ;;
+
+;;
--- /dev/null
+++ b/libstd/intparse.myr
@@ -1,0 +1,71 @@
+use "chartype.use"
+use "die.use"
+use "fmt.use"
+use "hasprefix.use"
+use "option.use"
+use "types.use"
+use "utf.use"
+
+pkg std =
+ generic intparsebase : (s : byte[:], base : int -> option(@a::(integral,numeric)))
+ generic intparse : (s : byte[:] -> option(@a::(integral,numeric)))
+;;
+
+generic intparse = {s
+ var isneg
+
+ isneg = false
+ if hasprefix(s, "-")
+ s = s[1:]
+ isneg = true
+ ;;
+
+ if hasprefix(s, "0x")
+ -> doparse(s[2:], isneg, 16)
+ elif hasprefix(s, "0o")
+ -> doparse(s[2:], isneg, 8)
+ elif hasprefix(s, "0b")
+ -> doparse(s[2:], isneg, 2)
+ else
+ -> doparse(s, isneg, 10)
+ ;;
+}
+
+generic intparsebase = {s, base
+ var isneg
+
+ isneg = false
+ if hasprefix(s, "-")
+ s = s[1:]
+ isneg = true
+ ;;
+
+ -> doparse(s, isneg, base)
+}
+
+generic doparse = {s, isneg, base
+ var c
+ var v
+ var cv : int32
+
+ v = 0
+ while s.len != 0
+ (c, s) = striter(s)
+ if c == '_'
+ continue
+ ;;
+ cv = charval(c, base)
+ if cv >= 0
+ v *= (base castto(@a::(integral,numeric)))
+ v += cv castto(@a::(integral,numeric))
+ else
+ -> `None
+ ;;
+ ;;
+
+ if isneg
+ -> `Some -v
+ else
+ -> `Some v
+ ;;
+}
--- /dev/null
+++ b/libstd/ipparse.myr
@@ -1,0 +1,74 @@
+use "die.use"
+use "intparse.use"
+use "option.use"
+use "strfind.use"
+use "types.use"
+use "chartype.use"
+
+ /* FIXME: needed for decls which should be pulled in as hidden */
+use "hasprefix.use"
+use "utf.use"
+
+pkg std =
+
+ type netaddr = union
+ `Ipv4 byte[4]
+ `Ipv6 byte[16]
+ ;;
+
+ const ipparse : (ip : byte[:] -> option(netaddr))
+ const ip4parse : (ip : byte[:] -> option(netaddr))
+ const ip6parse : (ip : byte[:] -> option(netaddr))
+;;
+
+const ipparse = {ip
+ match strfind(ip, ":")
+ | `Some _: -> ip6parse(ip)
+ | `None: -> ip4parse(ip)
+ ;;
+}
+
+const ip4parse = {ip
+ var addr
+ var last : size
+ var x : option(int32)
+ var val : int32 /* need int32 to check for overflow */
+ var i
+ var j : size
+
+ i = 0
+ last = 0
+ for j = 0; j < ip.len; j++
+ if ip[j] == '.' castto(byte)
+ match intparsebase(ip[last:j], 10)
+ | `Some v:
+ val = v
+ if val < 0 || val > 255
+ -> `None
+ ;;
+ addr[i++] = val castto(byte)
+ last = j + 1
+ | `None:
+ -> `None
+ ;;
+ ;;
+ ;;
+ match intparsebase(ip[last:j], 10)
+ | `Some v:
+ val = v
+ if val < 0 || val > 255
+ -> `None
+ ;;
+ addr[i] = val castto(byte)
+ | `None:
+ -> `None
+ ;;
+ if j != ip.len
+ -> `None
+ ;;
+ -> `Some (`Ipv4 addr)
+}
+
+const ip6parse = {ip
+ -> `None
+}
--- /dev/null
+++ b/libstd/mk.myr
@@ -1,0 +1,22 @@
+use "alloc.use"
+
+pkg std =
+ generic mk : (val : @a -> @a#)
+;;
+
+/* Takes a value, and heapifies it.
+
+FIXME: This depends on inlining and copy propagation
+in order to be efficient. Neither of those are
+currently implemented. That means that this function
+is not efficient.
+
+It's still damn convenient, though, so it's in.
+*/
+generic mk = {val
+ var p
+
+ p = alloc()
+ p# = val
+ -> p
+}
--- /dev/null
+++ b/libstd/now.myr
@@ -1,0 +1,22 @@
+use "sys.use"
+use "types.use"
+use "fmt.use"
+
+pkg std =
+ const now : (-> time)
+;;
+
+/* milliseconds since epoch */
+const now = {
+ var tm
+ var sec
+ var nsec
+
+ sec = tm.sec
+ nsec = tm.nsec castto(uint64)
+ if clock_gettime(`Clockrealtime, &tm) == 0
+ -> (sec*1000 + nsec/(1000*1000)) castto(time)
+ else
+ -> -1
+ ;;
+}
--- /dev/null
+++ b/libstd/option.myr
@@ -1,0 +1,11 @@
+use "types.use"
+use "fmt.use"
+use "varargs.use"
+
+pkg std =
+ type option(@a) = union
+ `Some @a
+ `None
+ ;;
+;;
+
--- /dev/null
+++ b/libstd/optparse.myr
@@ -1,0 +1,138 @@
+use "alloc.use"
+use "die.use"
+use "extremum.use"
+use "fmt.use"
+use "option.use"
+use "slpush.use"
+use "sys.use"
+use "types.use"
+use "utf.use"
+
+pkg std =
+ type optctx = struct
+ /* public variables */
+ args : byte[:][:]
+
+ /* data passed in */
+ optstr : byte[:]
+ optargs : byte[:][:]
+
+ /* state */
+ optdone : bool /* if we've seen '--', everything's an arg */
+ finished : bool /* if we've processed all the optargs */
+ argidx : size
+ curarg : byte[:]
+ ;;
+
+ const optinit : (optstr: byte[:], optargs : byte[:][:] -> optctx#)
+ const optnext : (ctx : optctx# -> [char, byte[:]])
+ const optdone : (ctx : optctx# -> bool)
+ const optfin : (ctx : optctx# -> byte[:][:])
+;;
+
+const optinit = {optstr, optargs
+ var ctx
+
+ ctx = alloc()
+ ctx.optstr= optstr
+ ctx.optargs =optargs
+
+ ctx.optdone = false
+ ctx.finished = false
+ ctx.argidx = 0
+ ctx.curarg = [][:]
+
+ ctx.args = [][:]
+
+ next(ctx)
+ -> ctx
+}
+
+const optfin = {ctx
+ var a
+
+ a = ctx.args
+ free(ctx)
+ -> a
+}
+
+const optnext = {ctx
+ var c
+ var arg
+
+ (c, ctx.curarg) = striter(ctx.curarg)
+
+ match optinfo(ctx, c)
+ | `None:
+ fatal(1, "Unexpected argument %c\n", c)
+ | `Some (true, needed):
+ /* -arg => '-a' 'rg' */
+ if ctx.curarg.len > 0
+ arg = ctx.curarg
+ ctx.curarg = ctx.curarg[ctx.curarg.len:]
+ next(ctx)
+ /* '-a rg' => '-a' 'rg' */
+ elif ctx.argidx < (ctx.optargs.len - 1)
+ arg = ctx.optargs[ctx.argidx + 1]
+ ctx.argidx++
+ next(ctx)
+ elif needed
+ put("Expected argument for %c\n", c)
+ exit(1)
+ ;;
+ | `Some (false, _):
+ arg = ""
+ if !ctx.curarg.len
+ next(ctx)
+ ;;
+ ;;
+
+
+ -> (c, arg)
+}
+
+const optdone = {ctx
+ -> !ctx.curarg.len && ctx.finished
+}
+
+const optinfo = {ctx, arg
+ var s
+ var c
+
+ s = ctx.optstr
+ while s.len != 0
+ (c, s) = striter(s)
+ if c == arg
+ (c, s) = striter(s)
+ /* mandatory arg */
+ if c == ':'
+ -> `Some (true, true)
+ /* optional arg */
+ elif c == '?'
+ -> `Some (true, false)
+ /* no arg */
+ else
+ -> `Some (false, false)
+ ;;
+ ;;
+ ;;
+ -> `None
+}
+
+const next = {ctx
+ var i
+
+ for i = ctx.argidx + 1; i < ctx.optargs.len; i++
+ if !ctx.optdone && decode(ctx.optargs[i]) == '-'
+ goto foundopt
+ else
+ ctx.args = slpush(ctx.args, ctx.optargs[i])
+ ;;
+ ;;
+ ctx.finished = true
+ -> false
+:foundopt
+ ctx.argidx = i
+ ctx.curarg = ctx.optargs[i][1:]
+ -> true
+}
--- /dev/null
+++ b/libstd/rand.myr
@@ -1,0 +1,161 @@
+use "die.use"
+use "fmt.use"
+use "types.use"
+use "alloc.use"
+/*
+ Translated from C by Ori Bernstein
+ */
+
+/*
+ A C-program for MT19937, with initialization improved 2002/1/26.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ Any feedback is very welcome.
+ http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+ email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+ */
+
+pkg std =
+ type rng
+
+ const mksrng : (seed : uint32 -> rng#)
+ generic rand : (rng : rng#, lo : @a::(numeric,integral), hi : @a::(numeric,integral) -> @a::(numeric,integral))
+ generic randN : (rng : rng# -> @a::(numeric,integral))
+ const rand32 : (rng : rng# -> uint32)
+;;
+
+type rng = struct
+ state : uint32[624]
+ i : uint32
+;;
+
+/* allocates and initializes a random number generator */
+const mksrng = {seed
+ var rng
+
+ rng = alloc()
+ init(rng, seed)
+ -> rng
+}
+
+/* initializes a random number generator from the seed `seed`. */
+const init = {rng, seed
+ var i
+
+ for i = 0; i < 624; i++
+ rng.state[i] = seed
+ seed = 1812433253 * (seed ^ (seed >> 30)) + i + 1
+ ;;
+ rng.i = i
+}
+
+/*
+ Generates a random integer from `rng` in the range [lo, hi),
+ returning the value. The range [lo, hi) must be positive,
+ nonempty, and the difference between hi and lo must be
+ less then 2^(type_bits - 1)
+*/
+generic rand = {rng, lo, hi -> @a::(integral,numeric)
+ var span, lim
+ var maxrand
+ var val
+
+ assert(hi - lo > 0, "rand.myr: range for random values must be >= 1")
+
+ span = hi - lo
+ maxrand = (1 << (8*sizeof(@a))) - 1 /* max for signed value */
+ if maxrand < 0 /* signed */
+ maxrand = (1 << (8*sizeof(@a)-1)) - 1 /* max for signed value */
+ ;;
+
+ lim = (maxrand/span)*span
+ val = (randN(rng) & maxrand)
+ while val > lim
+ val = (randN(rng) & maxrand)
+ ;;
+ -> val % span + lo
+}
+
+/*
+ Generates a random integer of any size from the
+ random number generator `rng`. The returned value
+ may be negative, if the type is signed.
+*/
+generic randN = {rng -> @a::(integral,numeric)
+ var i, val
+
+ val = 0
+ for i = 0; i < sizeof(@a)/4; i++
+ val <<= 8*sizeof(@a)
+ val |= rand32(rng) castto(@a::(integral,numeric))
+ ;;
+ -> val
+}
+
+/*
+ generates a 32 bit unsigned random number
+ from the random number generator `rng`.
+*/
+const rand32 = {rng
+ var x
+
+ if rng.i == 624
+ next(rng)
+ ;;
+ x = rng.state[rng.i]
+ rng.i++
+
+ x ^= x >> 11
+ x ^= (x << 7) & 0x9D2C5680
+ x ^= (x << 15) & 0xEFC60000
+ -> x ^ (x >> 18)
+}
+
+
+/* updates random number generator state when we tick over. */
+const next = {rng
+ var k
+ var y
+
+ for k = 0; k < 227; k++
+ y = (rng.state[k] & 0x80000000) | (rng.state[k + 1] & 0x7FFFFFFF)
+ rng.state[k] = rng.state[k + 397] ^ (y >> 1) ^ ((y & 1) * 0x9908B0DF)
+ ;;
+ for ; k < 623; k++
+ y = (rng.state[k] & 0x80000000) | (rng.state[k + 1] & 0x7FFFFFFF)
+ rng.state[k] = rng.state[k - 227] ^ (y >> 1) ^ ((y & 1) * 0x9908B0DF);
+ ;;
+ y = (rng.state[623] & 0x80000000) | (rng.state[0] & 0x7FFFFFFF)
+ rng.state[623] = rng.state[396] ^ (y >> 1) ^ ((y & 1) * 0x9908B0DF);
+ rng.i = 0
+}
--- /dev/null
+++ b/libstd/resolve.myr
@@ -1,0 +1,433 @@
+use "alloc.use"
+use "chartype.use"
+use "die.use"
+use "endian.use"
+use "result.use"
+use "extremum.use"
+use "fmt.use"
+use "hashfuncs.use"
+use "htab.use"
+use "ipparse.use"
+use "option.use"
+use "slcp.use"
+use "sleq.use"
+use "slpush.use"
+use "slurp.use"
+use "strfind.use"
+use "strsplit.use"
+use "strstrip.use"
+use "sys.use"
+use "types.use"
+use "utf.use"
+
+pkg std =
+ type rectype = uint16
+
+ const DnsA : rectype = 1 /* host address */
+ const DnsNS : rectype = 2 /* authoritative name server */
+ const DnsMD : rectype = 3 /* mail destination (Obsolete - use MX) */
+ const DnsMF : rectype = 4 /* mail forwarder (Obsolete - use MX) */
+ const DnsCNAME : rectype = 5 /* canonical name for an alias */
+ const DnsSOA : rectype = 6 /* marks the start of a zone of authority */
+ const DnsMB : rectype = 7 /* mailbox domain name (EXPERIMENTAL) */
+ const DnsMG : rectype = 8 /* mail group member (EXPERIMENTAL) */
+ const DnsMR : rectype = 9 /* mail rename domain name (EXPERIMENTAL) */
+ const DnsNULL : rectype = 10 /* null RR (EXPERIMENTAL) */
+ const DnsWKS : rectype = 11 /* well known service description */
+ const DnsPTR : rectype = 12 /* domain name pointer */
+ const DnsHINFO : rectype = 13 /* host information */
+ const DnsMINFO : rectype = 14 /* mailbox or mail list information */
+ const DnsMX : rectype = 15 /* mail exchange */
+ const DnsTXT : rectype = 16 /* text strings */
+ const DnsAAAA : rectype = 28 /* ipv6 host address */
+
+
+ type resolveerr = union
+ `Badhost
+ `Badsrv
+ `Badquery
+ `Badresp
+ ;;
+
+ type hostinfo = struct
+ fam : sockfam
+ stype : socktype
+ ttl : uint32
+ addr : netaddr
+ /*
+ flags : uint32
+ addr : sockaddr[:]
+ canon : byte[:]
+ */
+ ;;
+
+ const resolve : (host : byte[:] -> result(hostinfo[:], resolveerr))
+ const resolvemx : (host : byte[:] -> result(hostinfo[:], resolveerr))
+ const resolverec : (host : byte[:], t : rectype -> result(hostinfo[:], resolveerr))
+;;
+
+const Hostfile = "/etc/hosts"
+const Resolvfile = "/etc/resolv.conf"
+
+var hostmap : htab(byte[:], hostinfo)#
+var search : byte[:][:]
+var nameservers : netaddr[:]
+var inited : bool = false
+
+
+const resolve = {host
+ -> resolverec(host, DnsA)
+}
+
+const resolvemx = {host
+ -> resolverec(host, DnsMX)
+}
+
+const resolverec = {host, t
+ match hostfind(host)
+ | `Some hinf:
+ -> `Ok slpush([][:], hinf)
+ | `None:
+ -> dnsresolve(host, DnsA)
+ ;;
+}
+
+const hostfind = {host
+ if !inited
+ hostmap = mkht(strhash, streq)
+ loadhosts()
+ loadresolv()
+ inited = true
+ ;;
+ -> htget(hostmap, host)
+}
+
+const loadhosts = {
+ var h
+ var lines
+
+ match slurp(Hostfile)
+ | `Ok d: h = d
+ | `Fail m: ->
+ ;;
+
+ lines = strsplit(h, "\n")
+ for l in lines
+ /* trim comment */
+ match strfind(l, "#")
+ | `Some _idx: l = l[:_idx]
+ ;;
+
+ match word(l)
+ | `Some (ip, rest):
+ match ipparse(ip)
+ | `Some addr:
+ addhosts(addr, ip, rest)
+ ;;
+ | `None:
+ ;;
+ ;;
+ slfree(lines)
+}
+
+const addhosts = {addr, as, str
+ var hinf
+ var fam
+
+ match addr
+ | `Ipv4 _: fam = Afinet
+ | `Ipv6 _: fam = Afinet6
+ ;;
+ while true
+ match word(str)
+ | `Some (name, rest):
+ if !hthas(hostmap, name)
+ hinf = [
+ .fam=fam,
+ .stype = 0,
+ .ttl = 0,
+ .addr = addr
+ ]
+ htput(hostmap, name, hinf)
+ ;;
+ str = rest
+ | `None:
+ ->
+ ;;
+ ;;
+}
+
+const loadresolv = {
+ var h
+ var lines
+
+ match slurp(Resolvfile)
+ | `Ok d: h = d
+ | `Fail m: ->
+ ;;
+
+ lines = strsplit(h, "\n")
+ for l in lines
+ match strfind(l, "#")
+ | `Some _idx: l = l[:_idx]
+ | `None:
+ ;;
+
+ match word(l)
+ | `Some (cmd, rest):
+ if sleq(cmd, "nameserver")
+ addns(rest)
+ ;;
+ ;;
+ ;;
+ slfree(lines)
+}
+
+const addns = {rest
+ match word(rest)
+ | `Some (name, _):
+ match ipparse(name)
+ | `Some addr:
+ nameservers = slpush(nameservers, addr)
+ | `None:
+ ;;
+ ;;
+}
+
+const word = {s
+ var c, len
+
+ len = 0
+ s = strfstrip(s)
+ for c = decode(s[len:]); c != Badchar && !isblank(c); c = decode(s[len:])
+ len += charlen(c)
+ ;;
+ if len == 0
+ -> `None
+ else
+ -> `Some (s[:len], s[len:])
+ ;;
+}
+
+
+const dnsresolve = {host, t
+ var nsrv
+
+ if !valid(host)
+ -> `Fail (`Badhost)
+ ;;
+ for ns in nameservers
+ nsrv = dnsconnect(ns)
+ if nsrv >= 0
+ -> dnsquery(nsrv, host, t)
+ ;;
+ ;;
+ -> `Fail (`Badsrv)
+}
+
+const dnsconnect = {ns
+ match ns
+ | `Ipv4 addr: -> dnsconnectv4(addr)
+ | `Ipv6 addr: die("don't support ipv6 yet\n")
+ ;;
+}
+
+const dnsconnectv4 = {addr
+ var sa : sockaddr_in
+ var s
+ var status
+
+ s = socket(Afinet, Sockdgram, 0)
+ if s < 0
+ -> -1
+ ;;
+ sa.fam = Afinet
+ sa.port = hosttonet(53)
+ sa.addr = addr
+ status = connect(s, (&sa) castto(sockaddr#), sizeof(sockaddr_in))
+ if status < 0
+ -> -1
+ ;;
+ -> s
+}
+
+const dnsquery = {srv, host, t
+ var id
+ var r
+
+ id = tquery(srv, host, t)
+ r = rquery(srv, id)
+ -> r
+}
+
+const Qr : uint16 = 1 << 0
+const Aa : uint16 = 1 << 5
+const Tc : uint16 = 1 << 6
+const Rd : uint16 = 1 << 7
+const Ra : uint16 = 1 << 8
+
+var nextid : uint16 = 42
+const tquery = {srv, host, t
+ var pkt : byte[512] /* big enough */
+ var off : size
+
+ /* header */
+ off = 0
+ off += pack16(pkt[:], off, nextid) /* id */
+ off += pack16(pkt[:], off, Ra) /* flags */
+ off += pack16(pkt[:], off, 1) /* qdcount */
+ off += pack16(pkt[:], off, 0) /* ancount */
+ off += pack16(pkt[:], off, 0) /* nscount */
+ off += pack16(pkt[:], off, 0) /* arcount */
+
+ /* query */
+ off += packname(pkt[:], off, host) /* host */
+ off += pack16(pkt[:], off, t castto(uint16)) /* qtype: a record */
+ off += pack16(pkt[:], off, 0x1) /* qclass: inet4 */
+
+ write(srv, pkt[:off])
+ -> nextid++
+}
+
+const rquery = {srv, id
+ var pktbuf : byte[1024]
+ var pkt
+ var n
+
+ n = read(srv, pktbuf[:])
+ if n < 0
+ ;;
+ pkt = pktbuf[:n]
+ -> hosts(pkt, id)
+}
+
+const hosts = {pkt, id : uint16
+ var off
+ var v, q, a
+ var i
+ var hinf : hostinfo[:]
+
+ off = 0
+ /* parse header */
+ (v, off) = unpack16(pkt, off) /* id */
+ if v != id
+ -> `Fail (`Badresp)
+ ;;
+ (v, off) = unpack16(pkt, off) /* flags */
+ (q, off) = unpack16(pkt, off) /* qdcount */
+ (a, off) = unpack16(pkt, off) /* ancount */
+ (v, off) = unpack16(pkt, off) /* nscount */
+ (v, off) = unpack16(pkt, off) /* arcount */
+
+ /* skip past query records */
+ for i = 0; i < q; i++
+ off = skipname(pkt, off) /* name */
+ (v, off) = unpack16(pkt, off) /* type */
+ (v, off) = unpack16(pkt, off) /* class */
+ ;;
+
+ /* parse answer records */
+ hinf = slalloc(a castto(size))
+ for i = 0; i < a; i++
+ off = skipname(pkt, off) /* name */
+ (v, off) = unpack16(pkt, off) /* type */
+ (v, off) = unpack16(pkt, off) /* class */
+ (hinf[i].ttl, off) = unpack32(pkt, off) /* ttl */
+ (v, off) = unpack16(pkt, off) /* rdatalen */
+ /* the thing we're interested in: our IP address */
+ hinf[i].addr = `Ipv4 [pkt[off], pkt[off+1], pkt[off+2], pkt[off+3]]
+ off += 4;
+ ;;
+ -> `Ok hinf
+}
+
+
+const skipname = {pkt, off
+ var sz
+
+ for sz = pkt[off] castto(size); sz != 0; sz = pkt[off] castto(size)
+ /* ptr is 2 bytes */
+ if sz & 0xC0 == 0xC0
+ -> off + 2
+ else
+ off += sz + 1
+ ;;
+ ;;
+ -> off + 1
+}
+
+
+const pack16 = {buf, off, v
+ buf[off] = (v & 0xff00) >> 8 castto(byte)
+ buf[off+1] = (v & 0x00ff) castto(byte)
+ -> sizeof(uint16) /* we always write one uint16 */
+}
+
+const unpack16 = {buf, off
+ var v
+
+ v = (buf[off] castto(uint16)) << 8
+ v |= (buf[off + 1] castto(uint16))
+ -> (v, off+sizeof(uint16))
+}
+
+const unpack32 = {buf, off
+ var v
+
+ v = (buf[off] castto(uint32)) << 24
+ v |= (buf[off+1] castto(uint32)) << 32
+ v |= (buf[off+2] castto(uint32)) << 8
+ v |= (buf[off+3] castto(uint32))
+ -> (v, off+sizeof(uint32))
+}
+
+const packname = {buf, off : size, host
+ var i
+ var start
+ var last
+
+ start = off
+ last = 0
+ for i = 0; i < host.len; i++
+ if host[i] == ('.' castto(byte))
+ off += addseg(buf, off, host[last:i])
+ last = i + 1
+ ;;
+ ;;
+ if host[host.len - 1] != ('.' castto(byte))
+ off += addseg(buf, off, host[last:])
+ ;;
+ off += addseg(buf, off, "") /* null terminating segment */
+ -> off - start
+}
+
+const addseg = {buf, off, str
+ buf[off] = str.len castto(byte)
+ slcp(buf[off + 1 : off + str.len + 1], str)
+ -> str.len + 1
+}
+
+const valid = {host : byte[:]
+ var i
+ var seglen
+
+ /* maximum length: 255 chars */
+ if host.len > 255
+ -> false
+ ;;
+
+ seglen = 0
+ for i = 0; i < host.len; i++
+ if host[i] == ('.' castto(byte))
+ seglen = 0
+ ;;
+ if seglen > 63
+ -> false
+ ;;
+ if host[i] & 0x80 != 0
+ -> false
+ ;;
+ ;;
+
+ -> true
+}
--- /dev/null
+++ b/libstd/result.myr
@@ -1,0 +1,9 @@
+use "die.use"
+
+pkg std =
+ type result(@a, @b) = union
+ `Ok @a
+ `Fail @b
+ ;;
+;;
+
--- /dev/null
+++ b/libstd/search.myr
@@ -1,0 +1,42 @@
+use "cmp.use"
+use "option.use"
+use "fmt.use"
+
+pkg std =
+ generic lsearch : (sl : @t[:], val : @t, cmp : (a : @t, b : @t -> order) -> option(@idx::(integral,numeric)))
+ generic bsearch : (sl : @t[:], val : @t, cmp : (a : @t, b : @t -> order) -> option(@idx::(integral,numeric)))
+;;
+
+/* linear search over a list of values */
+generic lsearch = {sl, val, cmp
+ var i
+
+ for i = 0; i < sl.len; i++
+ match cmp(sl[i], val)
+ | `Equal:
+ -> `Some i
+ ;;
+ ;;
+ -> `None
+}
+
+/* binary search over a sorted list of values. */
+generic bsearch = {sl, val, cmp
+ var hi, lo, mid
+
+ lo = 0
+ hi = sl.len - 1
+
+ while lo <= hi
+ mid = (hi + lo) / 2
+ match cmp(val, sl[mid])
+ | `Before: hi = mid - 1
+ | `After: lo = mid + 1
+ | `Equal:
+ -> `Some mid
+ ;;
+ ;;
+ -> `None
+}
+
+
--- /dev/null
+++ b/libstd/slcp.myr
@@ -1,0 +1,26 @@
+use "die.use"
+use "types.use"
+
+pkg std =
+ generic slcp : (a : @a[:], b : @a[:] -> void)
+;;
+
+generic slcp = {a : @a[:], b : @a[:]
+ var i
+ var addr_a, addr_b
+
+ assert(a.len == b.len, "arguments to slcp() must be of equal length")
+
+ addr_a = a castto(@a#) castto(intptr)
+ addr_b = b castto(@a#) castto(intptr)
+ if addr_a <= addr_b
+ for i = 0; i < a.len; i++
+ a[i] = b[i]
+ ;;
+ else
+ for i = a.len; i > 0; i--
+ a[i - 1] = b[i - 1]
+ ;;
+ ;;
+
+}
--- /dev/null
+++ b/libstd/sldup.myr
@@ -1,0 +1,15 @@
+use "alloc.use"
+use "die.use"
+use "slcp.use"
+
+pkg std =
+ generic sldup : (sl : @a[:] -> @a[:])
+;;
+
+generic sldup = {sl
+ var ret
+
+ ret = slalloc(sl.len)
+ slcp(ret, sl)
+ -> ret
+}
--- /dev/null
+++ b/libstd/sleq.myr
@@ -1,0 +1,18 @@
+pkg std =
+ generic sleq : (a : @a[:], b : @a[:] -> bool)
+;;
+
+generic sleq = {a, b
+ var i
+
+ if a.len != b.len
+ -> false
+ ;;
+
+ for i = 0; i < a.len; i++
+ if a[i] != b[i]
+ -> false
+ ;;
+ ;;
+ -> true
+}
--- /dev/null
+++ b/libstd/slfill.myr
@@ -1,0 +1,12 @@
+pkg std =
+ generic slfill : (sl : @a[:], v : @a -> @a[:])
+;;
+
+generic slfill = {sl, v
+ var i
+
+ for i = 0; i < sl.len; i++
+ sl[i] = v
+ ;;
+ -> sl
+}
--- /dev/null
+++ b/libstd/sljoin.myr
@@ -1,0 +1,15 @@
+use "alloc.use"
+use "slcp.use"
+
+pkg std =
+ generic sljoin : (dst : @a[:], src : @a[:] -> @a[:])
+;;
+
+generic sljoin = {dst, src
+ var len
+
+ len = dst.len
+ dst = slgrow(dst, len + src.len)
+ slcp(dst[len:], src)
+ -> dst
+}
--- /dev/null
+++ b/libstd/slpush.myr
@@ -1,0 +1,13 @@
+use "types.use"
+use "alloc.use"
+use "fmt.use"
+
+pkg std =
+ generic slpush : (sl : @a[:], elt : @a -> @a[:])
+;;
+
+generic slpush = {sl, elt
+ sl = slgrow(sl, sl.len + 1)
+ sl[sl.len - 1] = elt
+ -> sl
+}
--- /dev/null
+++ b/libstd/slput.myr
@@ -1,0 +1,21 @@
+use "types.use"
+use "alloc.use"
+use "die.use"
+use "fmt.use"
+
+pkg std =
+ generic slput : (sl : @a[:], idx : size, elt : @a -> @a[:])
+;;
+
+generic slput = {sl, idx, elt
+ var i
+ var len
+
+ len = sl.len
+ sl = slgrow(sl, sl.len + 1)
+ for i = len - 1; i >= idx; i--
+ sl[i + 1] = sl[i]
+ ;;
+ sl[idx] = elt
+ -> sl
+}
--- /dev/null
+++ b/libstd/slurp.myr
@@ -1,0 +1,38 @@
+use "alloc.use"
+use "die.use"
+use "result.use"
+use "extremum.use"
+use "fmt.use"
+use "sys.use"
+use "types.use"
+
+pkg std =
+ const slurp : (path : byte[:] -> result(byte[:], byte[:]))
+;;
+
+const Bufinc = 4096
+
+const slurp = {path
+ var fd
+ var n
+ var len
+ var buf
+
+ fd = open(path, Ordonly)
+ if fd < 0
+ -> `Fail "Could not open file"
+ ;;
+
+ len = 0
+ buf = slalloc(Bufinc)
+ while true
+ n = read(fd, buf[len:])
+ if n == 0
+ goto done
+ ;;
+ len += n
+ buf = slgrow(buf, len + Bufinc)
+ ;;
+:done
+ -> `Ok buf[:len]
+}
--- /dev/null
+++ b/libstd/sort.myr
@@ -1,0 +1,59 @@
+use "cmp.use"
+
+pkg std =
+ generic sort : (sl:@a[:], cmp:(a:@a, b:@a -> order) -> @a[:])
+;;
+
+generic sort = {sl, cmp
+ var end
+ var tmp
+
+ heapify(sl, cmp)
+ end = sl.len - 1
+ while end > 0
+ tmp = sl[end]
+ sl[end] = sl[0]
+ sl[0] = tmp
+ end--
+ siftdown(sl[:end], 0, cmp)
+ ;;
+ -> sl
+}
+
+generic heapify = {sl, cmp
+ var start
+
+ start = sl.len/2 - 1
+ while start >= 0
+ siftdown(sl, start, cmp)
+ start--
+ ;;
+}
+
+generic siftdown = {sl, start, cmp
+ var r, c, s
+ var tmp
+
+ r = start
+ while 2*r + 1 <= sl.len
+ c = r*2 + 1
+ s = r
+ match cmp(sl[s], sl[c])
+ | `Before: s = c
+ ;;
+ if c + 1 < sl.len
+ match cmp(sl[s], sl[c + 1])
+ | `Before: s = c + 1
+ ;;
+ ;;
+ if s != r
+ tmp = sl[r]
+ sl[r] = sl[s]
+ sl[s] = tmp
+ r = s
+ else
+ ->
+ ;;
+ ;;
+}
+
--- /dev/null
+++ b/libstd/start-linux.s
@@ -1,0 +1,121 @@
+.data
+/* std._environment : byte[:][:] */
+.globl std$_environment
+std$_environment:
+.envbase:
+.quad 0 /* env size */
+.envlen:
+.quad 0 /* env ptr */
+
+.globl std$__cenvp
+std$__cenvp:
+.quad 0
+
+.text
+/*
+ * counts the length of the string pointed to
+ * by %r8, returning len in %r9. Does not modify
+ * any registers outside of %r9
+ */
+cstrlen:
+ xorq %r9,%r9
+ jmp .lentest
+
+ .lenloop:
+ incq %r9
+ .lentest:
+ cmpb $0,(%r8,%r9)
+ jne .lenloop
+ ret
+
+
+/*
+ * Counts the size of the null terminated string vector
+ * pointed to by %rbx. Clobbers %r10,%r11
+ */
+count:
+ xorq %r9,%r9
+ movq %rbx,%r11
+.countloop:
+ movq (%r11),%r10
+ testq %r10,%r10
+ jz .countdone
+ addq $1,%r9
+ addq $8,%r11
+ jmp .countloop
+.countdone:
+ ret
+
+/*
+ * iterate over the strings for argc, and put
+ * them into the args array.
+ *
+ * argc in %rax, argv in %rbx, dest vector in %rcx
+ */
+cvt:
+ jmp .cvttest
+.cvtloop:
+ subq $1,%rax
+ movq (%rbx),%r8
+ call cstrlen
+ movq %r8, (%rcx)
+ movq %r9, 8(%rcx)
+ addq $8, %rbx
+ addq $16, %rcx
+.cvttest:
+ testq %rax,%rax
+ jnz .cvtloop
+.cvtdone:
+ ret
+
+/*
+ * The entry point for the whole program.
+ * This is called by the OS. In order, it:
+ * - Sets up all argc entries as slices
+ * - Sets up all envp entries as slices
+ * - Converts argc/argv to a slice
+ * - Stashes envp in std._environment
+ * - Stashes a raw envp copy in __cenvp (for syscalls to use)
+ * - Calls main()
+ */
+.globl _start
+_start:
+ /* turn args into a slice */
+ movq %rsp,%rbp
+
+ /* stack allocate sizeof(byte[:])*(argc + len(envp)) */
+ movq (%rbp),%rax
+ leaq 16(%rbp,%rax,8), %rbx /* argp = argv + 8*argc + 8 */
+ call count
+ addq %r9,%rax
+ imulq $16,%rax
+ subq %rax,%rsp
+ movq %rsp, %rdx /* saved args[:] */
+
+ /* convert envp to byte[:][:] for std._environment */
+ movq (%rbp),%rax
+ leaq 16(%rbp,%rax,8), %rbx /* envp = argv + 8*argc + 8 */
+ /* store envp for some syscalls to use without converting */
+ movq %rbx,std$__cenvp(%rip)
+ movq %r9,%rax
+ movq %rsp, %rcx
+ movq %r9,.envlen
+ movq %rdx,.envbase
+ call cvt
+ movq %rcx,%rdx
+
+ /* convert argc, argv to byte[:][:] for args. */
+ movq (%rbp), %rax /* argc */
+ leaq 8(%rbp), %rbx /* argv */
+ movq (%rbp), %rsi /* saved argc */
+ call cvt
+ pushq %rsi
+ pushq %rdx
+
+ /* enter the main program */
+ call main
+ /* exit(0) */
+ xorq %rdi,%rdi
+ movq $60,%rax
+ syscall
+
--- /dev/null
+++ b/libstd/start-osx.s
@@ -1,0 +1,119 @@
+.data
+/* std._environment : byte[:][:] */
+.globl _std$_environment
+_std$_environment:
+.envbase:
+.quad 0 /* env size */
+.envlen:
+.quad 0 /* env ptr */
+
+.globl _std$__cenvp
+_std$__cenvp:
+.quad 0
+
+.text
+/*
+ * counts the length of the string pointed to
+ * by %r8, returning len in %r9. Does not modify
+ * any registers outside of %r9
+ */
+cstrlen:
+ xorq %r9,%r9
+ jmp .lentest
+
+ .lenloop:
+ incq %r9
+ .lentest:
+ cmpb $0,(%r8,%r9)
+ jne .lenloop
+ ret
+
+
+/*
+ * Counts the size of the null terminated string vector
+ * pointed to by %rbx. Clobbers %r10,%r11
+ */
+count:
+ xorq %r9,%r9
+ movq %rbx,%r11
+.countloop:
+ movq (%r11),%r10
+ testq %r10,%r10
+ jz .countdone
+ addq $1,%r9
+ addq $8,%r11
+ jmp .countloop
+.countdone:
+ ret
+
+/*
+ * iterate over the strings for argc, and put
+ * them into the args array.
+ *
+ * argc in %rax, argv in %rbx, dest vector in %rcx
+ */
+cvt:
+ jmp .cvttest
+.cvtloop:
+ subq $1,%rax
+ movq (%rbx),%r8
+ call cstrlen
+ movq %r8, (%rcx)
+ movq %r9, 8(%rcx)
+ addq $8, %rbx
+ addq $16, %rcx
+.cvttest:
+ testq %rax,%rax
+ jnz .cvtloop
+.cvtdone:
+ ret
+
+/*
+ * The entry point for the whole program.
+ * This is called by the OS. In order, it:
+ * - Sets up all argc entries as slices
+ * - Sets up all envp entries as slices
+ * - Converts argc/argv to a slice
+ * - Stashes envp in std._environment
+ * - Stashes a raw envp copy in __cenvp (for syscalls to use)
+ * - Calls main()
+ */
+.globl start
+start:
+ /* turn args into a slice */
+ movq %rsp,%rbp
+ /* stack allocate sizeof(byte[:])*(argc + len(envp)) */
+ movq (%rbp),%rax
+ leaq 16(%rbp,%rax,8), %rbx /* argp = argv + 8*argc + 8 */
+ call count
+ addq %r9,%rax
+ imulq $16,%rax
+ subq %rax,%rsp
+ movq %rsp, %rdx /* saved args[:] */
+
+ /* convert envp to byte[:][:] for std._environment */
+ movq (%rbp),%rax
+ leaq 16(%rbp,%rax,8), %rbx /* envp = argv + 8*argc + 8 */
+ movq %rbx,_std$__cenvp(%rip)
+ movq %r9,%rax
+ movq %rsp, %rcx
+ movq %r9,.envlen(%rip)
+ movq %rdx,.envbase(%rip)
+ call cvt
+ movq %rcx,%rdx
+
+ /* convert argc, argv to byte[:][:] for args. */
+ movq (%rbp), %rax /* argc */
+ leaq 8(%rbp), %rbx /* argv */
+ movq (%rbp), %rsi /* saved argc */
+ call cvt
+ pushq %rsi
+ pushq %rdx
+
+ /* enter the main program */
+ call _main
+ /* exit */
+ xorq %rdi,%rdi
+ movq $0x2000001,%rax
+ syscall
+
--- /dev/null
+++ b/libstd/strfind.myr
@@ -1,0 +1,26 @@
+use "types.use"
+use "option.use"
+
+pkg std =
+ const strfind : (haystack : byte[:], needle : byte[:] -> option(size))
+;;
+
+const strfind = {haystack, needle
+ var i, j
+
+ for i = 0; i < haystack.len; i++
+ if i + needle.len > haystack.len
+ -> `None
+ ;;
+ if haystack[i] == needle[0]
+ for j = 0; j < needle.len; j++
+ if haystack[i + j] != needle[j]
+ goto nextiter
+ ;;
+ ;;
+ -> `Some i
+ ;;
+:nextiter
+ ;;
+ -> `None
+}
--- /dev/null
+++ b/libstd/strjoin.myr
@@ -1,0 +1,41 @@
+use "alloc.use"
+use "die.use"
+use "slcp.use"
+
+pkg std =
+ const strcat : (a : byte[:], b : byte[:] -> byte[:])
+ const strjoin : (strings : byte[:][:], delim : byte[:] -> byte[:])
+;;
+
+const strcat = {a, b
+ -> strjoin([a, b][:], "")
+}
+
+const strjoin = {strings, delim
+ var len, off
+ var i
+ var s
+
+ len = 0
+ for i = 0; i < strings.len; i++
+ len += strings[i].len
+ ;;
+ if strings.len > 0
+ len += (strings.len - 1)*delim.len
+ ;;
+
+ s = slalloc(len)
+ off = 0
+ for i = 0; i < strings.len; i++
+ slcp(s[off:off + strings[i].len], strings[i])
+ off += strings[i].len
+ /* we don't want to terminate the last string with delim */
+ if i != strings.len - 1
+ slcp(s[off:off + delim.len], delim)
+ off += delim.len
+ ;;
+ ;;
+ -> s
+}
+
+
--- /dev/null
+++ b/libstd/strsplit.myr
@@ -1,0 +1,34 @@
+use "alloc.use"
+use "die.use"
+use "extremum.use"
+use "fmt.use"
+use "option.use"
+use "slpush.use"
+use "strfind.use"
+use "sys.use"
+use "types.use"
+
+pkg std =
+ const strsplit : (s : byte[:], delim : byte[:] -> byte[:][:])
+;;
+
+const strsplit = {s, delim
+ var last
+ var sp
+
+ sp = [][:]
+ last = 0
+ while true
+ match strfind(s, delim)
+ | `Some i:
+ sp = slpush(sp, s[:i])
+ s = s[i + delim.len:]
+ | `None:
+ goto donesplit
+ ;;
+ ;;
+:donesplit
+ sp = slpush(sp, s[:])
+ -> sp
+}
+
--- /dev/null
+++ b/libstd/strstrip.myr
@@ -1,0 +1,44 @@
+use "types.use"
+use "utf.use"
+use "chartype.use"
+
+pkg std =
+ const strstrip : (str : byte[:] -> byte[:])
+ const strfstrip : (str : byte[:] -> byte[:])
+ const strrstrip : (str : byte[:] -> byte[:])
+;;
+
+/* strip blanks from both head and tail of str */
+const strstrip = {str
+ -> strrstrip(strfstrip(str))
+}
+
+/* strip forward on str */
+const strfstrip = {str
+ var c
+
+ for c = decode(str); isblank(c); c = decode(str)
+ str = str[charlen(c):]
+ ;;
+ -> str
+
+}
+
+/* strip reverse on str */
+const strrstrip = {str
+ var i
+ var end
+
+ /* scan backwards for start of utf8 char */
+ end = str.len
+ for i = str.len; i != 0; i--
+ if str[i] & 0x80 == 0
+ if !isspace(decode(str[i-1:]))
+ goto donestrip
+ ;;
+ end = i - 1
+ ;;
+ ;;
+:donestrip
+ -> str[:end]
+}
--- /dev/null
+++ b/libstd/sys-linux.myr
@@ -1,0 +1,630 @@
+use "types.use"
+use "varargs.use"
+
+pkg std =
+ type pid = int64
+ type scno = int64 /* syscall */
+ type fdopt = int64 /* fd options */
+ type fd = int64 /* fd */
+ type mprot = int64 /* memory protection */
+ type mopt = int64 /* memory mapping options */
+ type socktype = int64 /* socket type */
+ type sockproto = int64 /* socket protocol */
+ type sockfam = uint16 /* socket family */
+ type whence = uint64
+
+ type clock = union
+ `Clockrealtime
+ `Clockmonotonic
+ `Clockproccpu
+ `Clockthreadcpu
+ `Clockmonotonicraw
+ `Clockrealtimecoarse
+ `Clockmonotoniccoarse
+ `Clockboottime
+ `Clockrealtimealarm
+ `Clockboottimealarm
+ ;;
+
+ type timespec = struct
+ sec : uint64
+ nsec : uint64
+ ;;
+
+ type timeval = struct
+ sec : uint64
+ usec : uint64
+ ;;
+
+ type rusage = struct
+ utime : timeval /* user time */
+ stime : timeval /* system time */
+ _opaque : uint64[14] /* padding (darwin-specific data) */
+ ;;
+
+ type statbuf = struct
+ dev : uint64
+ __inotrunc : uint32
+ __pad0 : uint32
+ mode : uint32
+ nlink : uint32
+ uid : uint32
+ gid : uint32
+ rdev : uint64
+ __pad1 : uint32
+ size : uint64
+ blksize : uint32
+ blocks : uint64
+ atime : uint64
+ atimens : uint64
+ mtime : uint64
+ mtimens : uint64
+ ctime : uint64
+ ctimens : uint64
+ ino : uint64
+ ;;
+
+ type utsname = struct
+ system : byte[65]
+ node : byte[65]
+ release : byte[65]
+ version : byte[65]
+ machine : byte[65]
+ domain : byte[65]
+ ;;
+
+ type sockaddr = struct
+ fam : sockfam
+ data : byte[14]
+ ;;
+
+ type sockaddr_in = struct
+ fam : sockfam
+ port : uint16
+ addr : byte[4]
+ zero : byte[8]
+ ;;
+
+ type sockaddr_storage = struct
+ fam : sockfam
+ __align : uint32
+ __pad : byte[112]
+ ;;
+
+ /* open options */
+ const Ordonly : fdopt = 0x0
+ const Owronly : fdopt = 0x1
+ const Ordwr : fdopt = 0x2
+ const Oappend : fdopt = 0x80
+ const Ocreat : fdopt = 0x40
+ const Onofollow : fdopt = 0x20000
+ const Ondelay : fdopt = 0x800
+ const Otrunc : fdopt = 0x200
+
+ /* mmap protection */
+ const Mprotnone : mprot = 0x0
+ const Mprotrd : mprot = 0x1
+ const Mprotwr : mprot = 0x2
+ const Mprotexec : mprot = 0x4
+ const Mprotrw : mprot = 0x3 /* convenience */
+
+ /* mmap options */
+ const Mshared : mopt = 0x1
+ const Mpriv : mopt = 0x2
+ const Mfixed : mopt = 0x10
+ const Mfile : mopt = 0x0
+ const Manon : mopt = 0x20
+ const M32bit : mopt = 0x40
+
+ /* socket families. INCOMPLETE. */
+ const Afunspec : sockfam = 0
+ const Afunix : sockfam = 1
+ const Afinet : sockfam = 2
+ const Afinet6 : sockfam = 10
+
+ /* socket types. */
+ const Sockstream : socktype = 1 /* sequenced, reliable byte stream */
+ const Sockdgram : socktype = 2 /* datagrams */
+ const Sockraw : socktype = 3 /* raw proto */
+ const Sockrdm : socktype = 4 /* reliably delivered messages */
+ const Sockseqpacket : socktype = 5 /* sequenced, reliable packets */
+ const Sockdccp : socktype = 6 /* data congestion control protocol */
+ const Sockpack : socktype = 10 /* linux specific packet */
+
+ const Seekset : whence = 0
+ const Seekcur : whence = 1
+ const Seekend : whence = 2
+
+ /* return value for a failed mapping */
+ const Mapbad : byte# = -1 castto(byte#)
+
+ /* syscalls */
+ const Sysread : scno = 0
+ const Syswrite : scno = 1
+ const Sysopen : scno = 2
+ const Sysclose : scno = 3
+ const Sysstat : scno = 4
+ const Sysfstat : scno = 5
+ const Syslstat : scno = 6
+ const Syspoll : scno = 7
+ const Syslseek : scno = 8
+ const Sysmmap : scno = 9
+ const Sysmprotect : scno = 10
+ const Sysmunmap : scno = 11
+ const Sysbrk : scno = 12
+ const Sysrt_sigaction : scno = 13
+ const Sysrt_sigprocmask : scno = 14
+ const Sysrt_sigreturn : scno = 15
+ const Sysioctl : scno = 16
+ const Syspread64 : scno = 17
+ const Syspwrite64 : scno = 18
+ const Sysreadv : scno = 19
+ const Syswritev : scno = 20
+ const Sysaccess : scno = 21
+ const Syspipe : scno = 22
+ const Sysselect : scno = 23
+ const Syssched_yield : scno = 24
+ const Sysmremap : scno = 25
+ const Sysmsync : scno = 26
+ const Sysmincore : scno = 27
+ const Sysmadvise : scno = 28
+ const Sysshmget : scno = 29
+ const Sysshmat : scno = 30
+ const Sysshmctl : scno = 31
+ const Sysdup : scno = 32
+ const Sysdup2 : scno = 33
+ const Syspause : scno = 34
+ const Sysnanosleep : scno = 35
+ const Sysgetitimer : scno = 36
+ const Sysalarm : scno = 37
+ const Syssetitimer : scno = 38
+ const Sysgetpid : scno = 39
+ const Syssendfile : scno = 40
+ const Syssocket : scno = 41
+ const Sysconnect : scno = 42
+ const Sysaccept : scno = 43
+ const Syssendto : scno = 44
+ const Sysrecvfrom : scno = 45
+ const Syssendmsg : scno = 46
+ const Sysrecvmsg : scno = 47
+ const Sysshutdown : scno = 48
+ const Sysbind : scno = 49
+ const Syslisten : scno = 50
+ const Sysgetsockname : scno = 51
+ const Sysgetpeername : scno = 52
+ const Syssocketpair : scno = 53
+ const Syssetsockopt : scno = 54
+ const Sysgetsockopt : scno = 55
+ const Sysclone : scno = 56
+ const Sysfork : scno = 57
+ const Sysvfork : scno = 58
+ const Sysexecve : scno = 59
+ const Sysexit : scno = 60
+ const Syswait4 : scno = 61
+ const Syskill : scno = 62
+ const Sysuname : scno = 63
+ const Syssemget : scno = 64
+ const Syssemop : scno = 65
+ const Syssemctl : scno = 66
+ const Sysshmdt : scno = 67
+ const Sysmsgget : scno = 68
+ const Sysmsgsnd : scno = 69
+ const Sysmsgrcv : scno = 70
+ const Sysmsgctl : scno = 71
+ const Sysfcntl : scno = 72
+ const Sysflock : scno = 73
+ const Sysfsync : scno = 74
+ const Sysfdatasync : scno = 75
+ const Systruncate : scno = 76
+ const Sysftruncate : scno = 77
+ const Sysgetdents : scno = 78
+ const Sysgetcwd : scno = 79
+ const Syschdir : scno = 80
+ const Sysfchdir : scno = 81
+ const Sysrename : scno = 82
+ const Sysmkdir : scno = 83
+ const Sysrmdir : scno = 84
+ const Syscreat : scno = 85
+ const Syslink : scno = 86
+ const Sysunlink : scno = 87
+ const Syssymlink : scno = 88
+ const Sysreadlink : scno = 89
+ const Syschmod : scno = 90
+ const Sysfchmod : scno = 91
+ const Syschown : scno = 92
+ const Sysfchown : scno = 93
+ const Syslchown : scno = 94
+ const Sysumask : scno = 95
+ const Sysgettimeofday : scno = 96
+ const Sysgetrlimit : scno = 97
+ const Sysgetrusage : scno = 98
+ const Syssysinfo : scno = 99
+ const Systimes : scno = 100
+ const Sysptrace : scno = 101
+ const Sysgetuid : scno = 102
+ const Syssyslog : scno = 103
+ const Sysgetgid : scno = 104
+ const Syssetuid : scno = 105
+ const Syssetgid : scno = 106
+ const Sysgeteuid : scno = 107
+ const Sysgetegid : scno = 108
+ const Syssetpgid : scno = 109
+ const Sysgetppid : scno = 110
+ const Sysgetpgrp : scno = 111
+ const Syssetsid : scno = 112
+ const Syssetreuid : scno = 113
+ const Syssetregid : scno = 114
+ const Sysgetgroups : scno = 115
+ const Syssetgroups : scno = 116
+ const Syssetresuid : scno = 117
+ const Sysgetresuid : scno = 118
+ const Syssetresgid : scno = 119
+ const Sysgetresgid : scno = 120
+ const Sysgetpgid : scno = 121
+ const Syssetfsuid : scno = 122
+ const Syssetfsgid : scno = 123
+ const Sysgetsid : scno = 124
+ const Syscapget : scno = 125
+ const Syscapset : scno = 126
+ const Sysrt_sigpending : scno = 127
+ const Sysrt_sigtimedwait : scno = 128
+ const Sysrt_sigqueueinfo : scno = 129
+ const Sysrt_sigsuspend : scno = 130
+ const Syssigaltstack : scno = 131
+ const Sysutime : scno = 132
+ const Sysmknod : scno = 133
+ const Sysuselib : scno = 134
+ const Syspersonality : scno = 135
+ const Sysustat : scno = 136
+ const Sysstatfs : scno = 137
+ const Sysfstatfs : scno = 138
+ const Syssysfs : scno = 139
+ const Sysgetpriority : scno = 140
+ const Syssetpriority : scno = 141
+ const Syssched_setparam : scno = 142
+ const Syssched_getparam : scno = 143
+ const Syssched_setscheduler : scno = 144
+ const Syssched_getscheduler : scno = 145
+ const Syssched_get_priority_max : scno = 146
+ const Syssched_get_priority_min : scno = 147
+ const Syssched_rr_get_interval : scno = 148
+ const Sysmlock : scno = 149
+ const Sysmunlock : scno = 150
+ const Sysmlockall : scno = 151
+ const Sysmunlockall : scno = 152
+ const Sysvhangup : scno = 153
+ const Sysmodify_ldt : scno = 154
+ const Syspivot_root : scno = 155
+ const Sys_sysctl : scno = 156
+ const Sysprctl : scno = 157
+ const Sysarch_prctl : scno = 158
+ const Sysadjtimex : scno = 159
+ const Syssetrlimit : scno = 160
+ const Syschroot : scno = 161
+ const Syssync : scno = 162
+ const Sysacct : scno = 163
+ const Syssettimeofday : scno = 164
+ const Sysmount : scno = 165
+ const Sysumount2 : scno = 166
+ const Sysswapon : scno = 167
+ const Sysswapoff : scno = 168
+ const Sysreboot : scno = 169
+ const Syssethostname : scno = 170
+ const Syssetdomainname : scno = 171
+ const Sysiopl : scno = 172
+ const Sysioperm : scno = 173
+ const Syscreate_module : scno = 174
+ const Sysinit_module : scno = 175
+ const Sysdelete_module : scno = 176
+ const Sysget_kernel_syms : scno = 177
+ const Sysquery_module : scno = 178
+ const Sysquotactl : scno = 179
+ const Sysnfsservctl : scno = 180
+ const Sysgetpmsg : scno = 181
+ const Sysputpmsg : scno = 182
+ const Sysafs_syscall : scno = 183
+ const Systuxcall : scno = 184
+ const Syssecurity : scno = 185
+ const Sysgettid : scno = 186
+ const Sysreadahead : scno = 187
+ const Syssetxattr : scno = 188
+ const Syslsetxattr : scno = 189
+ const Sysfsetxattr : scno = 190
+ const Sysgetxattr : scno = 191
+ const Syslgetxattr : scno = 192
+ const Sysfgetxattr : scno = 193
+ const Syslistxattr : scno = 194
+ const Sysllistxattr : scno = 195
+ const Sysflistxattr : scno = 196
+ const Sysremovexattr : scno = 197
+ const Syslremovexattr : scno = 198
+ const Sysfremovexattr : scno = 199
+ const Systkill : scno = 200
+ const Systime : scno = 201
+ const Sysfutex : scno = 202
+ const Syssched_setaffinity : scno = 203
+ const Syssched_getaffinity : scno = 204
+ const Sysset_thread_area : scno = 205
+ const Sysio_setup : scno = 206
+ const Sysio_destroy : scno = 207
+ const Sysio_getevents : scno = 208
+ const Sysio_submit : scno = 209
+ const Sysio_cancel : scno = 210
+ const Sysget_thread_area : scno = 211
+ const Syslookup_dcookie : scno = 212
+ const Sysepoll_create : scno = 213
+ const Sysepoll_ctl_old : scno = 214
+ const Sysepoll_wait_old : scno = 215
+ const Sysremap_file_pages : scno = 216
+ const Sysgetdents64 : scno = 217
+ const Sysset_tid_address : scno = 218
+ const Sysrestart_syscall : scno = 219
+ const Syssemtimedop : scno = 220
+ const Sysfadvise64 : scno = 221
+ const Systimer_create : scno = 222
+ const Systimer_settime : scno = 223
+ const Systimer_gettime : scno = 224
+ const Systimer_getoverrun : scno = 225
+ const Systimer_delete : scno = 226
+ const Sysclock_settime : scno = 227
+ const Sysclock_gettime : scno = 228
+ const Sysclock_getres : scno = 229
+ const Sysclock_nanosleep : scno = 230
+ const Sysexit_group : scno = 231
+ const Sysepoll_wait : scno = 232
+ const Sysepoll_ctl : scno = 233
+ const Systgkill : scno = 234
+ const Sysutimes : scno = 235
+ const Sysvserver : scno = 236
+ const Sysmbind : scno = 237
+ const Sysset_mempolicy : scno = 238
+ const Sysget_mempolicy : scno = 239
+ const Sysmq_open : scno = 240
+ const Sysmq_unlink : scno = 241
+ const Sysmq_timedsend : scno = 242
+ const Sysmq_timedreceive : scno = 243
+ const Sysmq_notify : scno = 244
+ const Sysmq_getsetattr : scno = 245
+ const Syskexec_load : scno = 246
+ const Syswaitid : scno = 247
+ const Sysadd_key : scno = 248
+ const Sysrequest_key : scno = 249
+ const Syskeyctl : scno = 250
+ const Sysioprio_set : scno = 251
+ const Sysioprio_get : scno = 252
+ const Sysinotify_init : scno = 253
+ const Sysinotify_add_watch : scno = 254
+ const Sysinotify_rm_watch : scno = 255
+ const Sysmigrate_pages : scno = 256
+ const Sysopenat : scno = 257
+ const Sysmkdirat : scno = 258
+ const Sysmknodat : scno = 259
+ const Sysfchownat : scno = 260
+ const Sysfutimesat : scno = 261
+ const Sysnewfstatat : scno = 262
+ const Sysunlinkat : scno = 263
+ const Sysrenameat : scno = 264
+ const Syslinkat : scno = 265
+ const Syssymlinkat : scno = 266
+ const Sysreadlinkat : scno = 267
+ const Sysfchmodat : scno = 268
+ const Sysfaccessat : scno = 269
+ const Syspselect6 : scno = 270
+ const Sysppoll : scno = 271
+ const Sysunshare : scno = 272
+ const Sysset_robust_list : scno = 273
+ const Sysget_robust_list : scno = 274
+ const Syssplice : scno = 275
+ const Systee : scno = 276
+ const Syssync_file_range : scno = 277
+ const Sysvmsplice : scno = 278
+ const Sysmove_pages : scno = 279
+ const Sysutimensat : scno = 280
+ const Sysepoll_pwait : scno = 281
+ const Syssignalfd : scno = 282
+ const Systimerfd_create : scno = 283
+ const Syseventfd : scno = 284
+ const Sysfallocate : scno = 285
+ const Systimerfd_settime : scno = 286
+ const Systimerfd_gettime : scno = 287
+ const Sysaccept4 : scno = 288
+ const Syssignalfd4 : scno = 289
+ const Syseventfd2 : scno = 290
+ const Sysepoll_create1 : scno = 291
+ const Sysdup3 : scno = 292
+ const Syspipe2 : scno = 293
+ const Sysinotify_init1 : scno = 294
+ const Syspreadv : scno = 295
+ const Syspwritev : scno = 296
+ const Sysrt_tgsigqueueinfo : scno = 297
+ const Sysperf_event_open : scno = 298
+ const Sysrecvmmsg : scno = 299
+ const Sysfanotify_init : scno = 300
+ const Sysfanotify_mark : scno = 301
+ const Sysprlimit64 : scno = 302
+ const Sysname_to_handle_at : scno = 303
+ const Sysopen_by_handle_at : scno = 304
+ const Sysclock_adjtime : scno = 305
+ const Syssyncfs : scno = 306
+ const Syssendmmsg : scno = 307
+ const Syssetns : scno = 308
+ const Sysgetcpu : scno = 309
+ const Sysprocess_vm_readv : scno = 310
+ const Sysprocess_vm_writev : scno = 311
+
+ /* network protocols */
+ const Ipproto_ip : sockproto = 0
+ const Ipproto_icmp : sockproto = 1
+ const Ipproto_tcp : sockproto = 6
+ const Ipproto_udp : sockproto = 17
+
+ /* getting to the os */
+ extern const syscall : (sc:scno, args:... -> int64)
+
+ /* process management */
+ const exit : (status:int -> void)
+ const getpid : ( -> int64)
+ const kill : (pid:int64, sig:int64 -> int64)
+ const fork : (-> int64)
+ const wait4 : (pid:int64, loc:int32#, opt : int64, usage:rusage# -> int64)
+ const waitpid : (pid:int64, loc:int32#, opt : int64 -> int64)
+ const execv : (cmd : byte[:], args : byte[:][:] -> int64)
+ const execve : (cmd : byte[:], args : byte[:][:], env : byte[:][:] -> int64)
+
+
+ /* fd manipulation */
+ const open : (path:byte[:], opts:fdopt -> fd)
+ const openmode : (path:byte[:], opts:fdopt, mode:int64 -> fd)
+ const close : (fd:fd -> int64)
+ const creat : (path:byte[:], mode:int64 -> fd)
+ const read : (fd:fd, buf:byte[:] -> size)
+ const write : (fd:fd, buf:byte[:] -> size)
+ const lseek : (fd:fd, off:uint64, whence:int64 -> int64)
+ const stat : (path:byte[:], sb:statbuf# -> int64)
+ const fstat : (fd:fd, sb:statbuf# -> int64)
+ const mkdir : (path : byte[:], mode : int64 -> int64)
+ const ioctl : (fd:fd, req : int64, args:... -> int64)
+
+ /* networking */
+ const socket : (dom : sockfam, stype : socktype, proto : sockproto -> fd)
+ const connect : (sock : fd, addr : sockaddr#, len : size -> int)
+ const accept : (sock : fd, addr : sockaddr#, len : size# -> fd)
+ const listen : (sock : fd, backlog : int -> int)
+ const bind : (sock : fd, addr : sockaddr#, len : size -> int)
+
+ /* memory mapping */
+ const munmap : (addr:byte#, len:size -> int64)
+ const mmap : (addr:byte#, len:size, prot:mprot, flags:mopt, fd:fd, off:off -> byte#)
+
+ /* time */
+ const clock_getres : (clk : clock, ts : timespec# -> int32)
+ const clock_gettime : (clk : clock, ts : timespec# -> int32)
+ const clock_settime : (clk : clock, ts : timespec# -> int32)
+ const sleep : (time : uint64 -> int32)
+ const nanosleep : (req : timespec#, rem : timespec# -> int32)
+
+ /* system information */
+ const uname : (buf : utsname# -> int)
+;;
+
+extern const cstring : (str : byte[:] -> byte#)
+extern const alloca : (sz : size -> byte#)
+extern const __cenvp : byte##
+
+/* process management */
+const exit = {status; syscall(Sysexit, status castto(int64))}
+const getpid = {; -> syscall(Sysgetpid, 1)}
+const kill = {pid, sig; -> syscall(Syskill, pid, sig)}
+const fork = {; -> syscall(Sysfork)}
+const wait4 = {pid, loc, opt, usage; -> syscall(Syswait4, pid, loc, opt, usage)}
+const waitpid = {pid, loc, opt;
+ var rusage
+ -> wait4(pid, loc, opt, &rusage)
+}
+
+const execv = {cmd, args
+ var p, cargs, i
+
+ /* of course we fucking have to duplicate this code everywhere,
+ * since we want to stack allocate... */
+ p = alloca((args.len + 1)*sizeof(byte#))
+ cargs = (p castto(byte##))[:args.len]
+ for i = 0; i < args.len; i++
+ cargs[i] = cstring(args[i])
+ ;;
+ cargs[args.len] = 0 castto(byte#)
+ -> syscall(Sysexecve, cstring(cmd), p, __cenvp)
+}
+
+const execve = {cmd, args, env
+ var cargs, cenv, i
+ var p
+
+ /* copy the args */
+ p = alloca((args.len + 1)*sizeof(byte#))
+ cargs = (p castto(byte##))[:args.len]
+ for i = 0; i < args.len; i++
+ cargs[i] = cstring(args[i])
+ ;;
+ cargs[args.len] = 0 castto(byte#)
+
+ /*
+ copy the env.
+ of course we fucking have to duplicate this code everywhere,
+ since we want to stack allocate...
+ */
+ p = alloca((env.len + 1)*sizeof(byte#))
+ cenv = (p castto(byte##))[:env.len]
+ for i = 0; i < env.len; i++
+ cenv[i] = cstring(env[i])
+ ;;
+ cenv[env.len] = 0 castto(byte#)
+
+ -> syscall(Sysexecve, cstring(cmd), p, cenv)
+}
+
+/* fd manipulation */
+const open = {path, opts; -> syscall(Sysopen, cstring(path), opts, 0o777) castto(fd)}
+const openmode = {path, opts, mode; -> syscall(Sysopen, cstring(path), opts, mode) castto(fd)}
+const close = {fd; -> syscall(Sysclose, fd)}
+const creat = {path, mode; -> syscall(Syscreat, cstring(path), mode) castto(fd)}
+const read = {fd, buf; -> syscall(Sysread, fd, buf castto(byte#), buf.len castto(size)) castto(size)}
+const write = {fd, buf; -> syscall(Syswrite, fd, buf castto(byte#), buf.len castto(size)) castto(size)}
+const lseek = {fd, off, whence; -> syscall(Syslseek, fd, off, whence)}
+const stat = {path, sb; -> syscall(Sysstat, cstring(path), sb)}
+const fstat = {fd, sb; -> syscall(Sysfstat, fd, sb)}
+const mkdir = {path, mode; -> syscall(Sysmkdir, cstring(path), mode) castto(int64)}
+const ioctl = {fd, req, args
+ var arg : byte#
+ var ap
+
+ ap = vastart(&args)
+ (arg, ap) = vanext(ap)
+ -> syscall(Sysioctl, fd, req, arg) castto(int64)
+}
+
+/* networking */
+const socket = {dom, stype, proto; -> syscall(Syssocket, dom castto(int64), stype, proto) castto(fd)}
+const connect = {sock, addr, len; -> syscall(Sysconnect, sock, addr, len) castto(int)}
+const bind = {sock, addr, len; -> syscall(Sysbind, sock, addr, len) castto(int)}
+const listen = {sock, backlog; -> syscall(Syslisten, sock, backlog castto(int64)) castto(int)}
+const accept = {sock, addr, lenp; -> syscall(Sysaccept, sock, addr, lenp) castto(fd)}
+
+/* memory mapping */
+const munmap = {addr, len; -> syscall(Sysmunmap, addr, len)}
+const mmap = {addr, len, prot, flags, fd, off; -> syscall(Sysmmap, addr, len, prot, flags, fd, off) castto(byte#)}
+
+/* time */
+const clock_getres = {clk, ts; -> syscall(Sysclock_getres, clockid(clk), ts) castto(int32)}
+const clock_gettime = {clk, ts; -> syscall(Sysclock_gettime, clockid(clk), ts) castto(int32)}
+const clock_settime = {clk, ts; -> syscall(Sysclock_settime, clockid(clk), ts) castto(int32)}
+
+const sleep = {time
+ var req, rem
+ req = [.sec = time, .nsec = 0]
+ -> nanosleep(&req, &rem)
+}
+
+const nanosleep = {req, rem;
+ -> syscall(Sysnanosleep, req, rem) castto(int32)
+}
+
+/* system information */
+const uname = {buf; -> syscall(Sysuname, buf) castto(int)}
+
+const clockid = {clk
+ match clk
+ | `Clockrealtime: -> 0
+ | `Clockmonotonic: -> 1
+ | `Clockproccpu: -> 2
+ | `Clockthreadcpu: -> 3
+ | `Clockmonotonicraw: -> 4
+ | `Clockrealtimecoarse: -> 5
+ | `Clockmonotoniccoarse:-> 6
+ | `Clockboottime: -> 7
+ | `Clockrealtimealarm: -> 8
+ | `Clockboottimealarm: -> 9
+ ;;
+ -> -1
+}
+
--- /dev/null
+++ b/libstd/sys-osx.myr
@@ -1,0 +1,750 @@
+use "types.use"
+use "varargs.use"
+
+pkg std =
+ type scno = int64 /* syscall */
+ type fdopt = int64 /* fd options */
+ type fd = int64 /* fd */
+ type mprot = int64 /* memory protection */
+ type mopt = int64 /* memory mapping options */
+ type socktype = int64 /* socket type */
+ type sockproto = int64 /* socket protocol */
+ type sockfam = uint8 /* socket family */
+
+ type timespec = struct
+ sec : uint64
+ nsec : uint32
+ ;;
+
+ type timeval = struct
+ sec : uint64
+ usec : uint32
+ ;;
+
+ type timezone = struct
+ minwest : int32 /* of greenwich */
+ dsttime : int32 /* nonzero if DST applies */
+ ;;
+
+ type clock = union
+ `Clockrealtime
+ `Clockmonotonic
+ ;;
+
+ type statbuf = struct
+ dev : int32
+ mode : uint16
+ nlink : uint32
+ ino : uint64 /* 32/64? which do I use? */
+ uid : uint32
+ gid : uint32
+ rdev : int32
+ atime : timespec
+ atimens : timespec
+ mtime : timespec
+ mtimens : timespec
+ ctime : timespec
+ ctimens : timespec
+ btime : timespec
+ btimens : timespec
+ size : off
+ blocks : uint
+ blocksz : uint
+ flags : uint32
+ gen : uint32
+ lspare : int32
+ qspare0 : int64
+ qspare1 : int64
+ ;;
+
+ type rusage = struct
+ utime : timeval /* user time */
+ stime : timeval /* system time */
+ _opaque : uint64[14] /* padding (darwin-specific data) */
+ ;;
+
+ type utsname = struct
+ system : byte[256]
+ node : byte[256]
+ release : byte[256]
+ version : byte[256]
+ machine : byte[256]
+ ;;
+
+ type sockaddr = struct
+ len : byte
+ fam : sockfam
+ data : byte[14] /* what is the *actual* length? */
+ ;;
+
+ type sockaddr_in = struct
+ len : byte
+ fam : sockfam
+ port : uint16
+ addr : byte[4]
+ zero : byte[8]
+ ;;
+
+ type sockaddr_storage = struct
+ len : byte
+ fam : sockfam
+ __pad1 : byte[6]
+ __align : uint64
+ __pad2 : byte[112]
+ ;;
+
+ type dirent = struct
+ ino : uint64
+ seekoff : uint64 /* seek offset (optional, used by servers) */
+ reclen : uint16 /* length of this record */
+ namlen : uint16 /* length of string in d_name */
+ typeid : uint8 /* file type, see below */
+ name : byte[1024]
+ ;;
+
+ /* open options */
+ const Ordonly : fdopt = 0x0
+ const Owronly : fdopt = 0x1
+ const Ordwr : fdopt = 0x2
+ const Ondelay : fdopt = 0x4
+ const Oappend : fdopt = 0x8
+ const Ocreat : fdopt = 0x200
+ const Onofollow : fdopt = 0x100
+ const Otrunc : fdopt = 0x400
+ const Odir : fdopt = 0x100000
+
+ /* mmap protection */
+ const Mprotnone : mprot = 0x0
+ const Mprotrd : mprot = 0x1
+ const Mprotwr : mprot = 0x2
+ const Mprotexec : mprot = 0x4
+ const Mprotrw : mprot = 0x3
+
+ /* mmap options */
+ const Mshared : mopt = 0x1
+ const Mpriv : mopt = 0x2
+ const Mfixed : mopt = 0x10
+ const Mfile : mopt = 0x0
+ const Manon : mopt = 0x1000
+ /* Only on Linux
+ const M32bit : mopt = 0x40
+ */
+
+ /* socket families. INCOMPLETE. */
+ const Afunspec : sockfam = 0
+ const Afunix : sockfam = 1
+ const Afinet : sockfam = 2
+ const Afinet6 : sockfam = 30
+
+ /* socket types. */
+ const Sockstream : socktype = 1
+ const Sockdgram : socktype = 2
+ const Sockraw : socktype = 3
+ const Sockrdm : socktype = 4
+ const Sockseqpacket : socktype = 5
+
+
+ /* return value for a failed mapping */
+ const Mapbad : byte# = -1 castto(byte#)
+
+ /* syscalls.
+ note, creat() implemented as open(path, Creat|Trunc|Wronly) */
+ const Syssyscall : scno = 0x2000000
+ const Sysexit : scno = 0x2000001
+ const Sysfork : scno = 0x2000002
+ const Sysread : scno = 0x2000003
+ const Syswrite : scno = 0x2000004
+ const Sysopen : scno = 0x2000005
+ const Sysclose : scno = 0x2000006
+ const Syswait4 : scno = 0x2000007
+ const Syslink : scno = 0x2000009
+ const Sysunlink : scno = 0x200000a
+ const Syschdir : scno = 0x200000c
+ const Sysfchdir : scno = 0x200000d
+ const Sysmknod : scno = 0x200000e
+ const Syschmod : scno = 0x200000f
+ const Syschown : scno = 0x2000010
+ const Sysgetfsstat : scno = 0x2000012
+ const Sysgetpid : scno = 0x2000014
+ const Syssetuid : scno = 0x2000017
+ const Sysgetuid : scno = 0x2000018
+ const Sysgeteuid : scno = 0x2000019
+ const Sysptrace : scno = 0x200001a
+ const Sysrecvmsg : scno = 0x200001b
+ const Syssendmsg : scno = 0x200001c
+ const Sysrecvfrom : scno = 0x200001d
+ const Sysaccept : scno = 0x200001e
+ const Sysgetpeername : scno = 0x200001f
+ const Sysgetsockname : scno = 0x2000020
+ const Sysaccess : scno = 0x2000021
+ const Syschflags : scno = 0x2000022
+ const Sysfchflags : scno = 0x2000023
+ const Syssync : scno = 0x2000024
+ const Syskill : scno = 0x2000025
+ const Sysgetppid : scno = 0x2000027
+ const Sysdup : scno = 0x2000029
+ const Syspipe : scno = 0x200002a
+ const Sysgetegid : scno = 0x200002b
+ const Sysprofil : scno = 0x200002c
+ const Syssigaction : scno = 0x200002e
+ const Sysgetgid : scno = 0x200002f
+ const Syssigprocmask : scno = 0x2000030
+ const Sysgetlogin : scno = 0x2000031
+ const Syssetlogin : scno = 0x2000032
+ const Sysacct : scno = 0x2000033
+ const Syssigpending : scno = 0x2000034
+ const Syssigaltstack : scno = 0x2000035
+ const Sysioctl : scno = 0x2000036
+ const Sysreboot : scno = 0x2000037
+ const Sysrevoke : scno = 0x2000038
+ const Syssymlink : scno = 0x2000039
+ const Sysreadlink : scno = 0x200003a
+ const Sysexecve : scno = 0x200003b
+ const Sysumask : scno = 0x200003c
+ const Syschroot : scno = 0x200003d
+ const Sysmsync : scno = 0x2000041
+ const Sysvfork : scno = 0x2000042
+ const Sysmunmap : scno = 0x2000049
+ const Sysmprotect : scno = 0x200004a
+ const Sysmadvise : scno = 0x200004b
+ const Sysmincore : scno = 0x200004e
+ const Sysgetgroups : scno = 0x200004f
+ const Syssetgroups : scno = 0x2000050
+ const Sysgetpgrp : scno = 0x2000051
+ const Syssetpgid : scno = 0x2000052
+ const Syssetitimer : scno = 0x2000053
+ const Sysswapon : scno = 0x2000055
+ const Sysgetitimer : scno = 0x2000056
+ const Sysgetdtablesize : scno = 0x2000059
+ const Sysdup2 : scno = 0x200005a
+ const Sysfcntl : scno = 0x200005c
+ const Sysselect : scno = 0x200005d
+ const Sysfsync : scno = 0x200005f
+ const Syssetpriority : scno = 0x2000060
+ const Syssocket : scno = 0x2000061
+ const Sysconnect : scno = 0x2000062
+ const Sysgetpriority : scno = 0x2000064
+ const Sysbind : scno = 0x2000068
+ const Syssetsockopt : scno = 0x2000069
+ const Syslisten : scno = 0x200006a
+ const Syssigsuspend : scno = 0x200006f
+ const Sysgettimeofday : scno = 0x2000074
+ const Sysgetrusage : scno = 0x2000075
+ const Sysgetsockopt : scno = 0x2000076
+ const Sysreadv : scno = 0x2000078
+ const Syswritev : scno = 0x2000079
+ const Syssettimeofday : scno = 0x200007a
+ const Sysfchown : scno = 0x200007b
+ const Sysfchmod : scno = 0x200007c
+ const Syssetreuid : scno = 0x200007e
+ const Syssetregid : scno = 0x200007f
+ const Sysrename : scno = 0x2000080
+ const Sysflock : scno = 0x2000083
+ const Sysmkfifo : scno = 0x2000084
+ const Syssendto : scno = 0x2000085
+ const Sysshutdown : scno = 0x2000086
+ const Syssocketpair : scno = 0x2000087
+ const Sysmkdir : scno = 0x2000088
+ const Sysrmdir : scno = 0x2000089
+ const Sysutimes : scno = 0x200008a
+ const Sysfutimes : scno = 0x200008b
+ const Sysadjtime : scno = 0x200008c
+ const Sysgethostuuid : scno = 0x200008e
+ const Syssetsid : scno = 0x2000093
+ const Sysgetpgid : scno = 0x2000097
+ const Syssetprivexec : scno = 0x2000098
+ const Syspread : scno = 0x2000099
+ const Syspwrite : scno = 0x200009a
+ const Sysnfssvc : scno = 0x200009b
+ const Sysstatfs : scno = 0x200009d
+ const Sysfstatfs : scno = 0x200009e
+ const Sysunmount : scno = 0x200009f
+ const Sysgetfh : scno = 0x20000a1
+ const Sysquotactl : scno = 0x20000a5
+ const Sysmount : scno = 0x20000a7
+ const Syscsops : scno = 0x20000a9
+ const Syswaitid : scno = 0x20000ad
+ const Sysadd_profil : scno = 0x20000b0
+ const Syskdebug_trace : scno = 0x20000b4
+ const Syssetgid : scno = 0x20000b5
+ const Syssetegid : scno = 0x20000b6
+ const Sysseteuid : scno = 0x20000b7
+ const Syssigreturn : scno = 0x20000b8
+ const Syschud : scno = 0x20000b9
+ const Sysfdatasync : scno = 0x20000bb
+ const Sysstat : scno = 0x20000bc
+ const Sysfstat : scno = 0x20000bd
+ const Syslstat : scno = 0x20000be
+ const Syspathconf : scno = 0x20000bf
+ const Sysfpathconf : scno = 0x20000c0
+ const Sysgetrlimit : scno = 0x20000c2
+ const Syssetrlimit : scno = 0x20000c3
+ const Sysgetdirentries : scno = 0x20000c4
+ const Sysmmap : scno = 0x20000c5
+ const Syslseek : scno = 0x20000c7
+ const Systruncate : scno = 0x20000c8
+ const Sysftruncate : scno = 0x20000c9
+ const Sys__sysctl : scno = 0x20000ca
+ const Sysmlock : scno = 0x20000cb
+ const Sysmunlock : scno = 0x20000cc
+ const Sysundelete : scno = 0x20000cd
+ const SysATsocket : scno = 0x20000ce
+ const SysATgetmsg : scno = 0x20000cf
+ const SysATputmsg : scno = 0x20000d0
+ const SysATPsndreq : scno = 0x20000d1
+ const SysATPsndrsp : scno = 0x20000d2
+ const SysATPgetreq : scno = 0x20000d3
+ const SysATPgetrsp : scno = 0x20000d4
+ const Sysmkcomplex : scno = 0x20000d8
+ const Sysstatv : scno = 0x20000d9
+ const Syslstatv : scno = 0x20000da
+ const Sysfstatv : scno = 0x20000db
+ const Sysgetattrlist : scno = 0x20000dc
+ const Syssetattrlist : scno = 0x20000dd
+ const Sysgetdirentriesattr : scno = 0x20000de
+ const Sysexchangedata : scno = 0x20000df
+ const Syssearchfs : scno = 0x20000e1
+ const Sysdelete : scno = 0x20000e2
+ const Syscopyfile : scno = 0x20000e3
+ const Sysfgetattrlist : scno = 0x20000e4
+ const Sysfsetattrlist : scno = 0x20000e5
+ const Syspoll : scno = 0x20000e6
+ const Syswatchevent : scno = 0x20000e7
+ const Syswaitevent : scno = 0x20000e8
+ const Sysmodwatch : scno = 0x20000e9
+ const Sysgetxattr : scno = 0x20000ea
+ const Sysfgetxattr : scno = 0x20000eb
+ const Syssetxattr : scno = 0x20000ec
+ const Sysfsetxattr : scno = 0x20000ed
+ const Sysremovexattr : scno = 0x20000ee
+ const Sysfremovexattr : scno = 0x20000ef
+ const Syslistxattr : scno = 0x20000f0
+ const Sysflistxattr : scno = 0x20000f1
+ const Sysfsctl : scno = 0x20000f2
+ const Sysinitgroups : scno = 0x20000f3
+ const Sysposix_spawn : scno = 0x20000f4
+ const Sysffsctl : scno = 0x20000f5
+ const Sysnfsclnt : scno = 0x20000f7
+ const Sysfhopen : scno = 0x20000f8
+ const Sysminherit : scno = 0x20000fa
+ const Syssemsys : scno = 0x20000fb
+ const Sysmsgsys : scno = 0x20000fc
+ const Sysshmsys : scno = 0x20000fd
+ const Syssemctl : scno = 0x20000fe
+ const Syssemget : scno = 0x20000ff
+ const Syssemop : scno = 0x2000100
+ const Sysmsgctl : scno = 0x2000102
+ const Sysmsgget : scno = 0x2000103
+ const Sysmsgsnd : scno = 0x2000104
+ const Sysmsgrcv : scno = 0x2000105
+ const Sysshmat : scno = 0x2000106
+ const Sysshmctl : scno = 0x2000107
+ const Sysshmdt : scno = 0x2000108
+ const Sysshmget : scno = 0x2000109
+ const Sysshm_open : scno = 0x200010a
+ const Sysshm_unlink : scno = 0x200010b
+ const Syssem_open : scno = 0x200010c
+ const Syssem_close : scno = 0x200010d
+ const Syssem_unlink : scno = 0x200010e
+ const Syssem_wait : scno = 0x200010f
+ const Syssem_trywait : scno = 0x2000110
+ const Syssem_post : scno = 0x2000111
+ const Syssem_getvalue : scno = 0x2000112
+ const Syssem_init : scno = 0x2000113
+ const Syssem_destroy : scno = 0x2000114
+ const Sysopen_extended : scno = 0x2000115
+ const Sysumask_extended : scno = 0x2000116
+ const Sysstat_extended : scno = 0x2000117
+ const Syslstat_extended : scno = 0x2000118
+ const Sysfstat_extended : scno = 0x2000119
+ const Syschmod_extended : scno = 0x200011a
+ const Sysfchmod_extended : scno = 0x200011b
+ const Sysaccess_extended : scno = 0x200011c
+ const Syssettid : scno = 0x200011d
+ const Sysgettid : scno = 0x200011e
+ const Syssetsgroups : scno = 0x200011f
+ const Sysgetsgroups : scno = 0x2000120
+ const Syssetwgroups : scno = 0x2000121
+ const Sysgetwgroups : scno = 0x2000122
+ const Sysmkfifo_extended : scno = 0x2000123
+ const Sysmkdir_extended : scno = 0x2000124
+ const Sysidentitysvc : scno = 0x2000125
+ const Sysshared_region_check_np : scno = 0x2000126
+ const Sysshared_region_map_np : scno = 0x2000127
+ const Sysvm_pressure_monitor : scno = 0x2000128
+ const Syspsynch_rw_longrdlock : scno = 0x2000129
+ const Syspsynch_rw_yieldwrlock : scno = 0x200012a
+ const Syspsynch_rw_downgrade : scno = 0x200012b
+ const Syspsynch_rw_upgrade : scno = 0x200012c
+ const Syspsynch_mutexwait : scno = 0x200012d
+ const Syspsynch_mutexdrop : scno = 0x200012e
+ const Syspsynch_cvbroad : scno = 0x200012f
+ const Syspsynch_cvsignal : scno = 0x2000130
+ const Syspsynch_cvwait : scno = 0x2000131
+ const Syspsynch_rw_rdlock : scno = 0x2000132
+ const Syspsynch_rw_wrlock : scno = 0x2000133
+ const Syspsynch_rw_unlock : scno = 0x2000134
+ const Syspsynch_rw_unlock2 : scno = 0x2000135
+ const Sysgetsid : scno = 0x2000136
+ const Syssettid_with_pid : scno = 0x2000137
+ const Sysaio_fsync : scno = 0x2000139
+ const Sysaio_return : scno = 0x200013a
+ const Sysaio_suspend : scno = 0x200013b
+ const Sysaio_cancel : scno = 0x200013c
+ const Sysaio_error : scno = 0x200013d
+ const Sysaio_read : scno = 0x200013e
+ const Sysaio_write : scno = 0x200013f
+ const Syslio_listio : scno = 0x2000140
+ const Sysiopolicysys : scno = 0x2000142
+ const Sysmlockall : scno = 0x2000144
+ const Sysmunlockall : scno = 0x2000145
+ const Sysissetugid : scno = 0x2000147
+ const Sys__pthread_kill : scno = 0x2000148
+ const Sys__pthread_sigmask : scno = 0x2000149
+ const Sys__sigwait : scno = 0x200014a
+ const Sys__disable_threadsignal : scno = 0x200014b
+ const Sys__pthread_markcancel : scno = 0x200014c
+ const Sys__pthread_canceled : scno = 0x200014d
+ const Sys__semwait_signal : scno = 0x200014e
+ const Sysproc_info : scno = 0x2000150
+ const Syssendfile : scno = 0x2000151
+ const Sysstat64 : scno = 0x2000152
+ const Sysfstat64 : scno = 0x2000153
+ const Syslstat64 : scno = 0x2000154
+ const Sysstat64_extended : scno = 0x2000155
+ const Syslstat64_extended : scno = 0x2000156
+ const Sysfstat64_extended : scno = 0x2000157
+ const Sysgetdirentries64 : scno = 0x2000158
+ const Sysstatfs64 : scno = 0x2000159
+ const Sysfstatfs64 : scno = 0x200015a
+ const Sysgetfsstat64 : scno = 0x200015b
+ const Sys__pthread_chdir : scno = 0x200015c
+ const Sys__pthread_fchdir : scno = 0x200015d
+ const Sysaudit : scno = 0x200015e
+ const Sysauditon : scno = 0x200015f
+ const Sysgetauid : scno = 0x2000161
+ const Syssetauid : scno = 0x2000162
+ const Sysgetaudit : scno = 0x2000163
+ const Syssetaudit : scno = 0x2000164
+ const Sysgetaudit_addr : scno = 0x2000165
+ const Syssetaudit_addr : scno = 0x2000166
+ const Sysauditctl : scno = 0x2000167
+ const Sysbsdthread_create : scno = 0x2000168
+ const Sysbsdthread_terminate : scno = 0x2000169
+ const Syskqueue : scno = 0x200016a
+ const Syskevent : scno = 0x200016b
+ const Syslchown : scno = 0x200016c
+ const Sysstack_snapshot : scno = 0x200016d
+ const Sysbsdthread_register : scno = 0x200016e
+ const Sysworkq_open : scno = 0x200016f
+ const Sysworkq_kernreturn : scno = 0x2000170
+ const Syskevent64 : scno = 0x2000171
+ const Sys__old_semwait_signal : scno = 0x2000172
+ const Sys__old_semwait_signal_nocancel : scno = 0x2000173
+ const Systhread_selfid : scno = 0x2000174
+ const Sys__mac_execve : scno = 0x200017c
+ const Sys__mac_syscall : scno = 0x200017d
+ const Sys__mac_get_file : scno = 0x200017e
+ const Sys__mac_set_file : scno = 0x200017f
+ const Sys__mac_get_link : scno = 0x2000180
+ const Sys__mac_set_link : scno = 0x2000181
+ const Sys__mac_get_proc : scno = 0x2000182
+ const Sys__mac_set_proc : scno = 0x2000183
+ const Sys__mac_get_fd : scno = 0x2000184
+ const Sys__mac_set_fd : scno = 0x2000185
+ const Sys__mac_get_pid : scno = 0x2000186
+ const Sys__mac_get_lcid : scno = 0x2000187
+ const Sys__mac_get_lctx : scno = 0x2000188
+ const Sys__mac_set_lctx : scno = 0x2000189
+ const Syssetlcid : scno = 0x200018a
+ const Sysgetlcid : scno = 0x200018b
+ const Sysread_nocancel : scno = 0x200018c
+ const Syswrite_nocancel : scno = 0x200018d
+ const Sysopen_nocancel : scno = 0x200018e
+ const Sysclose_nocancel : scno = 0x200018f
+ const Syswait4_nocancel : scno = 0x2000190
+ const Sysrecvmsg_nocancel : scno = 0x2000191
+ const Syssendmsg_nocancel : scno = 0x2000192
+ const Sysrecvfrom_nocancel : scno = 0x2000193
+ const Sysaccept_nocancel : scno = 0x2000194
+ const Sysmsync_nocancel : scno = 0x2000195
+ const Sysfcntl_nocancel : scno = 0x2000196
+ const Sysselect_nocancel : scno = 0x2000197
+ const Sysfsync_nocancel : scno = 0x2000198
+ const Sysconnect_nocancel : scno = 0x2000199
+ const Syssigsuspend_nocancel : scno = 0x200019a
+ const Sysreadv_nocancel : scno = 0x200019b
+ const Syswritev_nocancel : scno = 0x200019c
+ const Syssendto_nocancel : scno = 0x200019d
+ const Syspread_nocancel : scno = 0x200019e
+ const Syspwrite_nocancel : scno = 0x200019f
+ const Syswaitid_nocancel : scno = 0x20001a0
+ const Syspoll_nocancel : scno = 0x20001a1
+ const Sysmsgsnd_nocancel : scno = 0x20001a2
+ const Sysmsgrcv_nocancel : scno = 0x20001a3
+ const Syssem_wait_nocancel : scno = 0x20001a4
+ const Sysaio_suspend_nocancel : scno = 0x20001a5
+ const Sys__sigwait_nocancel : scno = 0x20001a6
+ const Sys__semwait_signal_nocancel : scno = 0x20001a7
+ const Sys__mac_mount : scno = 0x20001a8
+ const Sys__mac_get_mount : scno = 0x20001a9
+ const Sys__mac_getfsstat : scno = 0x20001aa
+ const Sysfsgetpath : scno = 0x20001ab
+ const Sysaudit_session_self : scno = 0x20001ac
+ const Sysaudit_session_join : scno = 0x20001ad
+ const Syspid_suspend : scno = 0x20001ae
+ const Syspid_resume : scno = 0x20001af
+ const Sysfileport_makeport : scno = 0x20001b0
+ const Sysfileport_makefd : scno = 0x20001b1
+
+ extern const syscall : (sc:scno, args:... -> int64)
+
+ /* process control */
+ const exit : (status:int -> void)
+ const getpid : ( -> int64)
+ const kill : (pid:int64, sig:int64 -> int64)
+ const fork : (-> int64)
+ const wait4 : (pid:int64, loc:int32#, opt : int64, usage:rusage# -> int64)
+ const waitpid : (pid:int64, loc:int32#, opt : int64 -> int64)
+ const execv : (cmd : byte[:], args : byte[:][:] -> int64)
+ const execve : (cmd : byte[:], args : byte[:][:], env : byte[:][:] -> int64)
+
+ /* fd manipulation */
+ const open : (path:byte[:], opts:fdopt -> fd)
+ const openmode : (path:byte[:], opts:fdopt, mode:int64 -> fd)
+ const close : (fd:fd -> int64)
+ const creat : (path:byte[:], mode:int64 -> fd)
+ const read : (fd:fd, buf:byte[:] -> size)
+ const write : (fd:fd, buf:byte[:] -> size)
+ const lseek : (fd:fd, off:uint64, whence:int64 -> int64)
+ const stat : (path:byte[:], sb:statbuf# -> int64)
+ const fstat : (fd:fd, sb:statbuf# -> int64)
+ const mkdir : (path : byte[:], mode : int64 -> int64)
+ const ioctl : (fd:fd, req : int64, args:... -> int64)
+ const getdirentries64 : (fd : fd, buf : byte[:], basep : uint64# -> int64)
+
+ /* networking */
+ const socket : (dom : sockfam, stype : socktype, proto : sockproto -> fd)
+ const connect : (sock : fd, addr : sockaddr#, len : size -> int)
+ const accept : (sock : fd, addr : sockaddr#, len : size# -> fd)
+ const listen : (sock : fd, backlog : int -> int)
+ const bind : (sock : fd, addr : sockaddr#, len : size -> int)
+
+ /* memory mapping */
+ const munmap : (addr:byte#, len:size -> int64)
+ const mmap : (addr:byte#, len:size, prot:mprot, flags:mopt, fd:fd, off:off -> byte#)
+
+ /* time */
+ const gettimeofday : (tv : timeval#, tz : timezone# -> int)
+ const settimeofday : (tv : timeval#, tz : timezone# -> int)
+ /* faked with gettimeofday */
+ const clock_getres : (clk : clock, ts : timespec# -> int)
+ const clock_gettime : (clk : clock, ts : timespec# -> int)
+ const clock_settime : (clk : clock, ts : timespec# -> int)
+
+ /* system information */
+ const uname : (buf : utsname# -> int)
+ const sysctl : (mib : int[:], old : byte[:]#, new : byte[:] -> int)
+;;
+
+extern const __osx_fork : (->int64)
+extern const cstring : (str : byte[:] -> byte#)
+extern const alloca : (sz : size -> byte#)
+extern const __cenvp : byte##
+
+/* process control */
+const exit = {status; syscall(Sysexit, status castto(int64))}
+const getpid = {; -> syscall(Sysgetpid, 1)}
+const kill = {pid, sig; -> syscall(Syskill, pid, sig)}
+const fork = {; -> __osx_fork()}
+const wait4 = {pid, loc, opt, usage; -> syscall(Syswait4, pid, loc, opt, usage)}
+const waitpid = {pid, loc, opt;
+ -> wait4(pid, loc, opt, 0 castto(rusage#))
+}
+
+const execv = {cmd, args
+ var p, cargs, i
+
+ /* doesn't just call execve() for efficiency's sake. */
+ p = alloca((args.len + 1)*sizeof(byte#))
+ cargs = (p castto(byte##))[:args.len]
+ for i = 0; i < args.len; i++
+ cargs[i] = cstring(args[i])
+ ;;
+ cargs[args.len] = 0 castto(byte#)
+ -> syscall(Sysexecve, cstring(cmd), p, __cenvp)
+}
+
+const execve = {cmd, args, env
+ var cargs, cenv, i
+ var p
+
+ /* copy the args */
+ p = alloca((args.len + 1)*sizeof(byte#))
+ cargs = (p castto(byte##))[:args.len]
+ for i = 0; i < args.len; i++
+ cargs[i] = cstring(args[i])
+ ;;
+ cargs[args.len] = 0 castto(byte#)
+
+ /*
+ copy the env.
+ of course we fucking have to duplicate this code everywhere,
+ since we want to stack allocate...
+ */
+ p = alloca((env.len + 1)*sizeof(byte#))
+ cenv = (p castto(byte##))[:env.len]
+ for i = 0; i < env.len; i++
+ cenv[i] = cstring(env[i])
+ ;;
+ cenv[env.len] = 0 castto(byte#)
+
+ -> syscall(Sysexecve, cstring(cmd), p, cenv)
+}
+
+
+/* fd manipulation */
+const open = {path, opts; -> syscall(Sysopen, cstring(path), opts, 0o777) castto(fd)}
+const openmode = {path, opts, mode; -> syscall(Sysopen, cstring(path), opts, mode) castto(fd)}
+const close = {fd; -> syscall(Sysclose, fd)}
+const creat = {path, mode; -> openmode(path, Ocreat | Otrunc | Owronly, mode) castto(fd)}
+const read = {fd, buf; -> syscall(Sysread, fd, buf castto(byte#), buf.len castto(size)) castto(size)}
+const write = {fd, buf; -> syscall(Syswrite, fd, buf castto(byte#), buf.len castto(size)) castto(size)}
+const lseek = {fd, off, whence; -> syscall(Syslseek, fd, off, whence)}
+const stat = {path, sb; -> syscall(Sysstat, cstring(path), sb)}
+const fstat = {fd, sb; -> syscall(Sysfstat, fd, sb)}
+const mkdir = {path, mode; -> syscall(Sysmkdir, cstring(path), mode) castto(int64)}
+const ioctl = {fd, req, args
+ var arg : byte#
+ var ap
+
+ ap = vastart(&args)
+ (arg, ap) = vanext(ap)
+ -> syscall(Sysioctl, fd, req, arg) castto(int64)
+}
+const getdirentries64 = {fd, buf, basep; -> syscall(Sysgetdirentries64, fd, buf castto(byte#), buf.len castto(size), basep)}
+
+/* networking */
+const socket = {dom, stype, proto; -> syscall(Syssocket, dom castto(int64), stype, proto) castto(fd) }
+const connect = {sock, addr, len; -> syscall(Sysconnect, sock, addr, len) castto(int)}
+const accept = {sock, addr, len; -> syscall(Sysaccept, sock, addr, len) castto(fd)}
+const listen = {sock, backlog; -> syscall(Syslisten, sock, backlog castto(int64)) castto(int)}
+const bind = {sock, addr, len; -> syscall(Sysbind, sock, addr, len) castto(int)}
+
+/* memory management */
+const munmap = {addr, len; -> syscall(Sysmunmap, addr, len)}
+const mmap = {addr, len, prot, flags, fd, off; -> syscall(Sysmmap, addr, len, prot, flags, fd, off) castto(byte#)}
+
+/* time */
+const gettimeofday = {tv, tz; -> syscall(Sysgettimeofday, tv, tz) castto(int)}
+const settimeofday = {tv, tz; -> syscall(Syssettimeofday, tv, tz) castto(int)}
+
+/* faked with gettimeofday */
+const clock_getres = {clk, ts
+ ts.sec = 0
+ ts.nsec = 1000*10 /* 10ms is reasonable resolution */
+ -> 0
+}
+
+const clock_gettime = {clk, ts
+ var tv
+ var ret
+
+ ret = gettimeofday(&tv, 0 castto(timezone#))
+ ts.sec = tv.sec
+ ts.nsec = tv.usec * 1000
+ -> ret
+}
+
+const clock_settime = {clk, ts
+ var tv
+
+ tv.sec = ts.sec
+ tv.usec = ts.nsec / 1000
+ -> settimeofday(&tv, 0 castto(timezone#))
+}
+
+/* system information */
+const uname = {buf;
+ buf.system[0] = 'D' castto(byte)
+ buf.system[1] = 'a' castto(byte)
+ buf.system[2] = 'r' castto(byte)
+ buf.system[3] = 'w' castto(byte)
+ buf.system[4] = 'i' castto(byte)
+ buf.system[5] = 'n' castto(byte)
+ buf.system[6] = 0
+ -> 0
+ /*
+ FIXME: THIS IS BROKEN. Miscompiled? DEBUG IT.
+ var mib : int[2]
+ var ret
+ var sys
+ var nod
+ var rel
+ var ver
+ var mach
+
+ ret = 0
+ mib[0] = 1 /* CTL_KERN */
+ mib[1] = 1 /* KERN_OSTYPE */
+ sys = buf.system[:]
+ if sysctl(mib[:], &sys, [][:]) < 0
+ ret = -1
+ ;;
+ std.put("%s\n", sys)
+
+ mib[0] = 1 /* CTL_KERN */
+ mib[1] = 10 /* KERN_HOSTNAME */
+ nod = buf.node[:]
+ if sysctl(mib[:], &nod, [][:]) < 0
+ ret = -1
+ ;;
+
+ mib[0] = 1 /* CTL_KERN */
+ mib[1] = 2 /* KERN_OSRELEASE */
+ rel = buf.release[:]
+ if sysctl(mib[:], &rel, [][:]) < 0
+ ret = -1
+ ;;
+
+ mib[0] = 1 /* CTL_KERN */
+ mib[1] = 4 /* KERN_VERSION */
+ ver = buf.version[:]
+ if sysctl(mib[:], &ver, [][:]) < 0
+ ret = -1
+ ;;
+
+ mib[0] = 6 /* CTL_HW */
+ mib[1] = 1 /* HW_MACHINE */
+ mach = buf.machine[:]
+ if sysctl(mib[:], &mach, [][:]) < 0
+ ret = -1
+ ;;
+
+ -> ret
+ */
+}
+
+const sysctl = {mib, old, new
+ var mibp
+ var mibsz
+ var o
+ var oldp
+ var oldsz
+ var newp
+ var newsz
+ var ret
+
+ mibp = mib castto(byte#)
+ mibsz = mib.len castto(uint64)
+ o = old#
+ oldp = o castto(byte#)
+ oldsz = o.len castto(uint64)
+ newp = new castto(byte#)
+ newsz = new castto(uint64)
+
+ ret = syscall(Sys__sysctl, mibp, mibsz, oldp, &oldsz, newp, newsz) castto(int)
+
+ old# = o[:oldsz]
+ -> ret
+}
--- /dev/null
+++ b/libstd/syscall-linux.s
@@ -1,0 +1,39 @@
+.globl std$syscall
+std$syscall:
+ pushq %rbp
+ pushq %rdi
+ pushq %rsi
+ pushq %rdx
+ pushq %r10
+ pushq %r8
+ pushq %r9
+ pushq %rcx
+ pushq %r11
+ /*
+ hack: We load 6 args regardless of
+ how many we actually have. This may
+ load junk values, but if the syscall
+ doesn't use them, it's going to be
+ harmless.
+ */
+ movq 80 (%rsp),%rax
+ movq 88 (%rsp),%rdi
+ movq 96 (%rsp),%rsi
+ movq 104(%rsp),%rdx
+ movq 112(%rsp),%r10
+ movq 120(%rsp),%r8
+ movq 128(%rsp),%r9
+
+ syscall
+
+ popq %r11
+ popq %rcx
+ popq %r9
+ popq %r8
+ popq %r10
+ popq %rdx
+ popq %rsi
+ popq %rdi
+ popq %rbp
+ ret
+
--- /dev/null
+++ b/libstd/syscall-osx.s
@@ -1,0 +1,85 @@
+.globl _std$syscall
+_std$syscall:
+ pushq %rbp
+ pushq %rdi
+ pushq %rsi
+ pushq %rdx
+ pushq %r10
+ pushq %r8
+ pushq %r9
+ pushq %rcx
+ pushq %r11
+ /*
+ hack: We load 6 args regardless of
+ how many we actually have. This may
+ load junk values, but if the syscall
+ doesn't use them, it's going to be
+ harmless.
+ */
+ movq 80 (%rsp),%rax
+ movq 88 (%rsp),%rdi
+ movq 96 (%rsp),%rsi
+ movq 104(%rsp),%rdx
+ movq 112(%rsp),%r10
+ movq 120(%rsp),%r8
+ movq 128(%rsp),%r9
+
+ syscall
+ jae success
+ negq %rax
+
+success:
+ popq %r11
+ popq %rcx
+ popq %r9
+ popq %r8
+ popq %r10
+ popq %rdx
+ popq %rsi
+ popq %rdi
+ popq %rbp
+ ret
+
+/*
+ * OSX is dumb about fork, and needs an assembly wrapper.
+ * The fork() syscall, when called directly, returns the pid in both
+ * processes, which means that both parent and child think they're
+ * the parent.
+ *
+ * checking this involves peeking in %edx, so we need to do this in asm.
+ */
+.globl _std$__osx_fork
+_std$__osx_fork:
+ pushq %rbp
+ pushq %rdi
+ pushq %rsi
+ pushq %rdx
+ pushq %r10
+ pushq %r8
+ pushq %r9
+ pushq %rcx
+ pushq %r11
+
+ movq $0x2000002,%rax
+ syscall
+
+ jae forksuccess
+ negq %rax
+
+forksuccess:
+ testl %edx,%edx
+ jz isparent
+ xorq %rax,%rax
+isparent:
+
+ popq %r11
+ popq %rcx
+ popq %r9
+ popq %r8
+ popq %r10
+ popq %rdx
+ popq %rsi
+ popq %rdi
+ popq %rbp
+ ret
+
--- /dev/null
+++ b/libstd/test.myr
@@ -1,0 +1,130 @@
+use std
+
+const ntstr = {s
+ var n
+
+ n = 0
+ while s[n] != 0 && n < s.len
+ n++
+ ;;
+ -> s[:n]
+}
+
+const main = {args : byte[:][:]
+ var x : byte#[1024]
+ var sz
+ var i
+ var ctx
+ var o
+ var a
+ var buf
+
+ std.put("The time is %l seconds past the epoch\n", std.now()/1000);
+ std.uname(&buf)
+ std.put("And you are running on:\n")
+ std.put("\tsystem:\t\"%s\"\n", ntstr(buf.system[:]))
+ std.put("\tnode:\t\"%s\"\n", ntstr(buf.node[:]))
+ std.put("\trelease:\t\"%s\"\n", ntstr(buf.release[:]))
+ std.put("\tmachine:\t\"%s\"\n", ntstr(buf.machine[:]))
+ ctx = std.optinit("asdf:g?h", args)
+ std.put("arglen = %i\n", ctx.args.len)
+ while !std.optdone(ctx)
+ (o, a) = std.optnext(ctx)
+ if o == 'h'
+ usage()
+ ;;
+ std.put("option %c, arg = %s\n", o, a)
+ ;;
+
+ std.put("env.len = %i\n", std._environment.len)
+ for i = 0; i < std._environment.len; i++
+ std.put("env[%i] = %s\n", i, std._environment[i])
+ ;;
+ std.put("args.len = %i\n", args.len)
+ for i = 0; i < args.len; i++
+ std.put("args[%i] = %s\n", i, args[i])
+ ;;
+
+ for i = 0; i < ctx.args.len; i++
+ std.put("arg %s\n", ctx.args[i])
+ ;;
+ printenv("SHELL")
+
+
+ /* try the byte allocator for large variety of sizes. */
+ for sz = 1; sz < 65536; sz *= 2
+ for i = 0; i < 1024; i++
+ x[i] = std.bytealloc(sz)
+ ;;
+ for i = 0; i < 1024; i++
+ std.bytefree(x[i], sz)
+ ;;
+ ;;
+
+ /* make sure the generic allocator works */
+ for i = 0; i < 1024; i++
+ x[i] = std.alloc()
+ ;;
+ for i = 0; i < 1024; i++
+ std.free(x[i])
+ ;;
+ std.write(1, "Hello, 世界\n")
+ chartypes()
+ testrng()
+ std.put("format output %i %i %s %s\n", 123, 321, "asdf", "מִלָּה")
+ std.put("format with no args\n")
+}
+
+const chartypes = {
+ var s
+ var c
+ var buf : byte[32]
+
+ s = " 1世界 äa\n"
+ while s.len != 0
+ (c, s) = std.striter(s)
+ if std.isspace(c)
+ std.write(1, "Space\n")
+ elif std.isalpha(c)
+ std.write(1, "Alpha\n")
+ elif std.isnum(c)
+ std.write(1, "Num\n")
+ else
+ std.write(1, "Dunno\n")
+ ;;
+ if !std.encode(buf[:std.charlen(c)], c)
+ std.write(1, "couldn't encode\n")
+ ;;
+ std.write(1, buf[:std.charlen(c)])
+ std.write(1, "\n")
+ ;;
+ if !std.encode(buf[0:3], -1)
+ std.write(1, "couldn't encode\n")
+ ;;
+}
+
+const testrng = {
+ var r
+ var i
+
+ r = std.mksrng(10)
+ for i = 0; i < 300; i++
+ std.put("r[%i] = %l\n", i, std.rand(r, 5, 10) castto(int64))
+ ;;
+ std.put("\n");
+}
+
+const usage = {
+ std.put("Pokes a bit at the standard library.\n")
+ std.put("Option string is asdf:g?h\n")
+ std.exit(0)
+}
+
+const printenv = {name
+ match std.getenv(name)
+ | `std.Some env:
+ std.put("Value of %s is %s\n", name, env)
+ | `std.None:
+ std.put("No env var %s is set\n", name)
+ ;;
+}
--- /dev/null
+++ b/libstd/try.myr
@@ -1,0 +1,13 @@
+use "option.use"
+use "fmt.use"
+
+pkg std =
+ generic try : (v : option(@a) -> @a)
+;;
+
+generic try = {v
+ match v
+ | `Some x: -> x
+ | `None: fatal(1, "expected `Some @a, got `None\n")
+ ;;
+}
--- /dev/null
+++ b/libstd/types.myr
@@ -1,0 +1,7 @@
+pkg std =
+ type size = uint64 /* spans entire address space */
+ type ssize = int64 /* signed size */
+ type off = uint64 /* file offsets */
+ type intptr = uint64 /* can hold any pointer losslessly */
+ type time = int64 /* milliseconds since epoch */
+;;
--- /dev/null
+++ b/libstd/units.myr
@@ -1,0 +1,11 @@
+pkg std =
+ /* JEDEC 100B.1 memory sizes */
+ generic KiB : @a::(integral,numeric) = 1024
+ generic MiB : @a::(integral,numeric) = KiB*1024
+ generic GiB : @a::(integral,numeric) = MiB*1024
+ generic TiB : @a::(integral,numeric) = GiB*1024
+ generic PiB : @a::(integral,numeric) = TiB*1024
+ generic EiB : @a::(integral,numeric) = PiB*1024
+ generic ZiB : @a::(integral,numeric) = EiB*1024
+ generic YiB : @a::(integral,numeric) = ZiB*1024
+;;
--- /dev/null
+++ b/libstd/utf.myr
@@ -1,0 +1,104 @@
+use "die.use"
+use "sys.use"
+use "types.use"
+
+pkg std =
+ const Badchar : char = -1 castto(char)
+ const Maxcharlen : size = 4
+ const Maxcharval : char = 0x10FFFF
+
+ const charlen : (chr : char -> size)
+ const encode : (buf : byte[:], chr : char -> size)
+ const decode : (buf : byte[:] -> char)
+ const striter : (str : byte[:] -> [char, byte[:]])
+;;
+
+const charlen = {c
+ if c < 0x80
+ -> 1
+ elif c < 0x800
+ -> 2
+ elif c < 0x10000
+ -> 3
+ elif c < 0x200000
+ -> 4
+ else
+ -> -1
+ ;;
+}
+
+const encode = {buf, c
+ var len
+ var mark
+ var i
+
+ len = charlen(c)
+ if len < 0 || buf.len < len
+ -> -1
+ ;;
+
+ if (len == 1)
+ mark = 0
+ else
+ mark = (((1 << (8 - len)) - 1) ^ 0xff) castto(char)
+ ;;
+
+ for i = len - 1; i > 0; i--
+ buf[i] = (c & 0x3f | 0x80) castto(byte)
+ c >>= 6
+ ;;
+
+ buf[0] = (c | mark) castto(byte)
+ -> len
+}
+
+const decode = {buf
+ var c
+ var b
+
+ (c, b) = striter(buf)
+ -> c
+}
+
+const striter = {str
+ var len
+ var mask
+ var chr
+ var i
+ var c
+ var tmp
+
+ if !str.len
+ /* empty string: no resync needed */
+ -> (Badchar, str)
+ ;;
+ c = str[0]
+ len = 0
+ if c & 0x80 == 0 /* 0b0xxx_xxxx */
+ len = 1
+ elif c & 0xe0 == 0xc0 /* 0b110x_xxxx */
+ len = 2
+ elif c & 0xf0 == 0xe0 /* 0b1110_xxxx */
+ len = 3
+ elif c & 0xf8 == 0xf0 /* 0b1111_0xxx */
+ len = 4
+ else
+ /* skip one char forward so we can try
+ resyncing the character stream */
+ -> (Badchar, str[1:])
+ ;;
+
+ if len == 0 || len > str.len
+ /* again, we want to try to resync */
+ -> (Badchar, str[1:])
+ ;;
+
+ mask = (1 << (8 - len)) - 1
+ chr = (c castto(uint32)) & mask
+ for i = 1; i < len; i++
+ tmp = str[i] castto(uint32)
+ chr = (chr << 6) | (tmp & 0x3f)
+ ;;
+
+ -> (chr castto(char), str[len:])
+}
--- /dev/null
+++ b/libstd/util.s
@@ -1,0 +1,47 @@
+/*
+ * Allocates a C string on the stack, for
+ * use within system calls, which is the only
+ * place the Myrddin stack should need nul-terminated
+ * strings.
+ *
+ * This is in assembly, because for efficiency we
+ * allocate the C strings on the stack, and don't adjust
+ * %rsp when returning.
+ */
+.globl std$cstring
+.globl _std$cstring
+_std$cstring:
+std$cstring:
+ movq (%rsp),%r15 /* ret addr */
+ movq 8(%rsp),%rsi /* src */
+ movq 16(%rsp),%rcx /* len */
+
+ subq %rcx,%rsp /* get stack */
+ movq %rsp,%rdi /* dest */
+ movq %rsp,%rax /* ret val */
+ subq $16,%rsp /* "unpop" the args */
+ subq $1,%rsp /* nul */
+ andq $(~15),%rsp /* align */
+
+ cld
+ rep movsb
+ movb $0,(%rdi) /* terminate */
+
+ pushq %r15 /* ret addr */
+ ret
+
+.globl std$alloca
+.globl _std$alloca
+_std$alloca:
+std$alloca:
+ movq (%rsp),%r15 /* ret addr */
+ movq 8(%rsp),%rbx /* len */
+
+ /* get stack space */
+ subq %rbx,%rsp /* get stack space */
+ movq %rsp,%rax /* top of stack (return value) */
+ subq $16,%rsp /* "unpop" the args for return */
+ andq $(~15),%rsp /* align */
+
+ pushq %r15 /* ret addr */
+ ret
--- /dev/null
+++ b/libstd/varargs.myr
@@ -1,0 +1,52 @@
+use "types.use"
+
+pkg std =
+ type valist
+
+ const vastart : (args : ...# -> valist)
+ generic vanext : (ap : valist -> [@a, valist])
+;;
+
+type valist = byte#
+
+/*
+ * a valist is really just a pointer to the varargs.
+ * we assume that these sit on the stack nicely,
+ * and don't need special handling to get to.
+ *
+ * This will be a problem when we switch to a
+ * register based convention. We might want to
+ * force varargs onto the stack regardless.
+ */
+const vastart = {args
+ -> args castto(valist)
+}
+
+generic vanext = {ap -> [@a, valist]
+ var v : @a
+ var align
+ var p
+
+ /*
+ Assumptions about the ABI:
+ * all types smaller than a word are
+ * aligned to their own size. Larger
+ * types are aligned to word size.
+ */
+ if sizeof(@a) > 8
+ align = 8
+ else
+ align = sizeof(@a)
+ ;;
+
+ /* apply the alignment to the arg pointer */
+ p = ap castto(intptr)
+ p = (p + align - 1) & ~(align - 1)
+ ap = p castto(valist)
+
+ v = (ap castto(@a#))#
+
+ /* only move on after we read through the value */
+ ap = ((p castto(intptr)) + sizeof(@a)) castto(valist)
+ -> (v, ap)
+}
--- /dev/null
+++ b/libstd/waitstatus-linux.myr
@@ -1,0 +1,22 @@
+use "die.use"
+pkg std =
+ type waitstatus = union
+ `Waitexit int32
+ `Waitsig int32
+ `Waitstop int32
+ ;;
+
+ const waitstatus : (st : int32 -> waitstatus)
+;;
+
+const waitstatus = {st
+ if st & 0x7f == 0 /* if exited */
+ -> `Waitexit ((st & 0xff00) >> 8)
+ elif ((st & 0xffff)-1) < 0xff /* if signaled */
+ -> `Waitsig ((st) & 0x7f)
+ elif (((st & 0xffff)*0x10001)>>8) > 0x7f00
+ -> `Waitstop ((st & 0xff00) >> 8)
+ ;;
+ die("unreachable")
+}
+
--- /dev/null
+++ b/libstd/waitstatus-osx.myr
@@ -1,0 +1,19 @@
+use "die.use"
+pkg std =
+ type waitstatus = union
+ `Waitexit int32
+ `Waitsig int32
+ `Waitstop int32
+ ;;
+
+ const waitstatus : (st : int32 -> waitstatus)
+;;
+
+const waitstatus = {st
+ match st & 0o177
+ | 0: -> `Waitexit (st >> 8)
+ | 0o177:-> `Waitstop (st >> 8)
+ | sig: -> `Waitsig sig
+ ;;
+ die("unreachable")
+}
--- /dev/null
+++ b/mi/Makefile
@@ -1,0 +1,8 @@
+LIB=libmi.a
+OBJ=cfg.o \
+ fold.o \
+ df.o \
+
+DEPS=../parse/libparse.a
+
+include ../mk/c.mk
--- /dev/null
+++ b/mi/cfg.c
@@ -1,0 +1,196 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+#include "opt.h"
+
+
+static Bb *mkbb(Cfg *cfg)
+{
+ Bb *bb;
+
+ bb = zalloc(sizeof(Bb));
+ bb->id = cfg->nextbbid++;
+ bb->pred = mkbs();
+ bb->succ = mkbs();
+ lappend(&cfg->bb, &cfg->nbb, bb);
+ 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 label(Cfg *cfg, Node *lbl, Bb *bb)
+{
+ htput(cfg->lblmap, lblstr(lbl), bb);
+ lappend(&bb->lbls, &bb->nlbls, lblstr(lbl));
+}
+
+static int addnode(Cfg *cfg, Bb *bb, Node *n)
+{
+ switch (exprop(n)) {
+ case Ojmp:
+ case Ocjmp:
+ lappend(&bb->nl, &bb->nnl, n);
+ lappend(&cfg->fixjmp, &cfg->nfixjmp, n);
+ lappend(&cfg->fixblk, &cfg->nfixblk, bb);
+ return 1;
+ break;
+ default:
+ lappend(&bb->nl, &bb->nnl, n);
+ break;
+ }
+ return 0;
+}
+
+static int islabel(Node *n)
+{
+ Node *l;
+ if (n->type != Nexpr)
+ return 0;
+ if (exprop(n) != Olit)
+ return 0;
+ l = n->expr.args[0];
+ if (l->type != Nlit)
+ return 0;
+ if (l->lit.littype != Llbl)
+ return 0;
+ return 1;
+}
+
+static Bb *addlabel(Cfg *cfg, Bb *bb, Node **nl, size_t i)
+{
+ /* if the current block assumes fall-through, insert an explicit jump */
+ if (i > 0 && nl[i - 1]->type == Nexpr) {
+ if (exprop(nl[i - 1]) != Ocjmp && exprop(nl[i - 1]) != Ojmp)
+ addnode(cfg, bb, mkexpr(-1, Ojmp, mklbl(-1, lblstr(nl[i])), NULL));
+ }
+ if (bb->nnl)
+ bb = mkbb(cfg);
+ label(cfg, nl[i], bb);
+ return bb;
+}
+
+Cfg *mkcfg(Node **nl, size_t nn)
+{
+ Cfg *cfg;
+ Bb *pre, *post;
+ Bb *bb, *targ;
+ Node *a, *b;
+ size_t i;
+
+ cfg = zalloc(sizeof(Cfg));
+ cfg->lblmap = mkht(strhash, streq);
+ pre = mkbb(cfg);
+ bb = mkbb(cfg);
+ for (i = 0; i < nn; i++) {
+ switch (nl[i]->type) {
+ case Nexpr:
+ if (islabel(nl[i]))
+ bb = addlabel(cfg, bb, nl, i);
+ else if (addnode(cfg, bb, nl[i]))
+ bb = mkbb(cfg);
+ break;
+ break;
+ case Ndecl:
+ break;
+ default:
+ die("Invalid node type %s in mkcfg", nodestr(nl[i]->type));
+ }
+ }
+ post = mkbb(cfg);
+ bsput(pre->succ, cfg->bb[1]->id);
+ bsput(cfg->bb[1]->pred, pre->id);
+ bsput(cfg->bb[cfg->nbb - 2]->succ, post->id);
+ bsput(post->pred, cfg->bb[cfg->nbb - 2]->id);
+ for (i = 0; i < cfg->nfixjmp; i++) {
+ bb = cfg->fixblk[i];
+ switch (exprop(cfg->fixjmp[i])) {
+ case Ojmp:
+ a = cfg->fixjmp[i]->expr.args[0];
+ b = NULL;
+ break;
+ case Ocjmp:
+ a = cfg->fixjmp[i]->expr.args[1];
+ b = cfg->fixjmp[i]->expr.args[2];
+ break;
+ default:
+ die("Bad jump fix thingy");
+ break;
+ }
+ if (a) {
+ targ = htget(cfg->lblmap, lblstr(a));
+ if (!targ)
+ die("No bb with label \"%s\"", lblstr(a));
+ bsput(bb->succ, targ->id);
+ bsput(targ->pred, bb->id);
+ }
+ if (b) {
+ targ = htget(cfg->lblmap, lblstr(b));
+ if (!targ)
+ die("No bb with label \"%s\"", lblstr(b));
+ bsput(bb->succ, targ->id);
+ bsput(targ->pred, bb->id);
+ }
+ }
+ return cfg;
+}
+
+void dumpcfg(Cfg *cfg, FILE *fd)
+{
+ size_t i, j;
+ Bb *bb;
+ char *sep;
+
+ for (j = 0; j < cfg->nbb; j++) {
+ bb = cfg->bb[j];
+ fprintf(fd, "\n");
+ fprintf(fd, "Bb: %d labels=(", bb->id);
+ sep = "";
+ for (i = 0; i < bb->nlbls; i++) {;
+ fprintf(fd, "%s%s", bb->lbls[i], sep);
+ sep = ",";
+ }
+ fprintf(fd, ")\n");
+
+ /* in edges */
+ fprintf(fd, "Pred: ");
+ sep = "";
+ for (i = 0; i < bsmax(bb->pred); i++) {
+ if (bshas(bb->pred, i)) {
+ fprintf(fd, "%s%zd", sep, i);
+ sep = ",";
+ }
+ }
+ fprintf(fd, "\n");
+
+ /* out edges */
+ fprintf(fd, "Succ: ");
+ sep = "";
+ for (i = 0; i < bsmax(bb->succ); i++) {
+ if (bshas(bb->succ, i)) {
+ fprintf(fd, "%s%zd", sep, i);
+ sep = ",";
+ }
+ }
+ fprintf(fd, "\n");
+
+ for (i = 0; i < bb->nnl; i++)
+ dump(bb->nl[i], fd);
+ fprintf(fd, "\n");
+ }
+}
--- /dev/null
+++ b/mi/df.c
@@ -1,0 +1,40 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+#include "opt.h"
+
+/*
+static void nodeuse(Node *n, Bitset *bs)
+{
+}
+
+static void nodedef(Node *n, Bitset *bs)
+{
+}
+
+static void bbuse(Bb *bb, Bitset *bs)
+{
+}
+
+static void bbdef(Bb *bb, Bitset *bs)
+{
+}
+*/
+
+void flow(Cfg *cfg)
+{
+}
+
+void checkret(Cfg *cfg)
+{
+}
--- /dev/null
+++ b/mi/fold.c
@@ -1,0 +1,212 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+#include "opt.h"
+
+static int islit(Node *n, vlong *v)
+{
+ Node *l;
+
+ if (exprop(n) != Olit)
+ return 0;
+ l = n->expr.args[0];
+ if (l->lit.littype != Lint)
+ return 0;
+ *v = l->lit.intval;
+ return 1;
+}
+
+static int isval(Node *n, vlong val)
+{
+ vlong v;
+
+ if (!islit(n, &v))
+ return 0;
+ return v == val;
+}
+
+static Node *val(int line, vlong val, Type *t)
+{
+ Node *n;
+
+ n = mkint(line, val);
+ n = mkexpr(line, Olit, n, NULL);
+ n->expr.type = t;
+ return n;
+}
+
+static int issmallconst(Node *dcl)
+{
+ Type *t;
+
+ if (!dcl->decl.isconst)
+ return 0;
+ if (!dcl->decl.init)
+ return 0;
+ t = tybase(exprtype(dcl->decl.init));
+ if (t->type <= Tyfloat64)
+ return 1;
+ return 0;
+}
+
+static Node *foldcast(Node *n)
+{
+ Type *to, *from;
+ Node *sub;
+
+ sub = n->expr.args[0];
+ to = exprtype(n);
+ from = exprtype(sub);
+
+ switch (tybase(to)->type) {
+ case Tybool:
+ case Tyint8: case Tyint16: case Tyint32: case Tyint64:
+ case Tyuint8: case Tyuint16: case Tyuint32: case Tyuint64:
+ case Tyint: case Tyuint: case Tylong: case Tyulong:
+ case Tychar: case Tybyte:
+ case Typtr:
+ switch (tybase(from)->type) {
+ case Tybool:
+ case Tyint8: case Tyint16: case Tyint32: case Tyint64:
+ case Tyuint8: case Tyuint16: case Tyuint32: case Tyuint64:
+ case Tyint: case Tyuint: case Tylong: case Tyulong:
+ case Tychar: case Tybyte:
+ case Typtr:
+ if (exprop(sub) == Olit || tybase(from)->type == tybase(to)->type) {
+ sub->expr.type = to;
+ return sub;
+ } else {
+ return n;
+ }
+ default:
+ return n;
+ }
+ default:
+ return n;
+ }
+ return n;
+}
+
+
+
+Node *fold(Node *n, int foldvar)
+{
+ Node **args, *r;
+ Type *t;
+ vlong a, b;
+ size_t i;
+
+ if (!n)
+ return NULL;
+ if (n->type != Nexpr)
+ return n;
+
+ r = NULL;
+ args = n->expr.args;
+ for (i = 0; i < n->expr.nargs; i++)
+ args[i] = fold(args[i], foldvar);
+ switch (exprop(n)) {
+ case Ovar:
+ if (foldvar && issmallconst(decls[n->expr.did]))
+ r = fold(decls[n->expr.did]->decl.init, foldvar);
+ break;
+ case Oadd:
+ /* x + 0 = 0 */
+ if (isval(args[0], 0))
+ r = args[1];
+ if (isval(args[1], 0))
+ r = args[0];
+ if (islit(args[0], &a) && islit(args[1], &b))
+ r = val(n->line, a + b, exprtype(n));
+ break;
+ case Osub:
+ /* x - 0 = 0 */
+ if (isval(args[1], 0))
+ r = args[0];
+ if (islit(args[0], &a) && islit(args[1], &b))
+ r = val(n->line, a - b, exprtype(n));
+ break;
+ case Omul:
+ /* 1 * x = x */
+ if (isval(args[0], 1))
+ r = args[1];
+ if (isval(args[1], 1))
+ r = args[0];
+ /* 0 * x = 0 */
+ if (isval(args[0], 0))
+ r = args[0];
+ if (isval(args[1], 0))
+ r = args[1];
+ if (islit(args[0], &a) && islit(args[1], &b))
+ r = val(n->line, a * b, exprtype(n));
+ break;
+ case Odiv:
+ /* x/1 = x */
+ if (isval(args[1], 1))
+ r = args[0];
+ /* 0/x = 0 */
+ if (isval(args[1], 0))
+ r = args[1];
+ if (islit(args[0], &a) && islit(args[1], &b))
+ r = val(n->line, a / b, exprtype(n));
+ break;
+ case Omod:
+ /* x%1 = x */
+ if (isval(args[1], 0))
+ r = args[0];
+ if (islit(args[0], &a) && islit(args[1], &b))
+ r = val(n->line, a % b, exprtype(n));
+ break;
+ case Oneg:
+ if (islit(args[0], &a))
+ r = val(n->line, -a, exprtype(n));
+ break;
+ case Obsl:
+ if (islit(args[0], &a) && islit(args[1], &b))
+ r = val(n->line, a << b, exprtype(n));
+ break;
+ case Obsr:
+ if (islit(args[0], &a) && islit(args[1], &b))
+ r = val(n->line, a >> b, exprtype(n));
+ break;
+ case Obor:
+ if (islit(args[0], &a) && islit(args[1], &b))
+ r = val(n->line, a | b, exprtype(n));
+ break;
+ case Oband:
+ if (islit(args[0], &a) && islit(args[1], &b))
+ r = val(n->line, a & b, exprtype(n));
+ break;
+ case Obxor:
+ if (islit(args[0], &a) && islit(args[1], &b))
+ r = val(n->line, a ^ b, exprtype(n));
+ break;
+ case Omemb:
+ t = tybase(exprtype(args[0]));
+ /* we only fold lengths right now */
+ if (t->type == Tyarray && !strcmp(namestr(args[1]), "len"))
+ r = t->asize;
+ break;
+ case Ocast:
+ r = foldcast(n);
+ break;
+ default:
+ break;
+ }
+
+ if (r)
+ return r;
+ else
+ return n;
+}
+
--- /dev/null
+++ b/mi/opt.h
@@ -1,0 +1,34 @@
+typedef struct Cfg Cfg;
+typedef struct Bb Bb;
+
+struct Cfg {
+ Bb **bb;
+ Bb *start;
+ Bb *end;
+ size_t nbb;
+
+ /* for building bb */
+ int nextbbid;
+ Htab *lblmap; /* label => Bb mapping */
+ Node **fixjmp;
+ size_t nfixjmp;
+ Bb **fixblk;
+ size_t nfixblk;
+};
+
+struct Bb {
+ int id;
+ char **lbls;
+ size_t nlbls;
+ Node **nl;
+ size_t nnl;
+ Bitset *pred;
+ Bitset *succ;
+};
+
+/* expression folding */
+Node *fold(Node *n, int foldvar);
+/* Takes a reduced block, and returns a flow graph. */
+Cfg *mkcfg(Node **nl, size_t nn);
+void dumpcfg(Cfg *c, FILE *fd);
+void flow(Cfg *cfg);
--- /dev/null
+++ b/mk/c.mk
@@ -1,0 +1,113 @@
+.DEFAULT_GOAL=all
+_DEPSDIR = .deps
+_DEPS=$(addprefix $(_DEPSDIR)/, $(OBJ:.o=.d))
+
+_LIBSRCHPATHS=$(addprefix -L, $(dir $(DEPS)))
+_LIBINCPATHS=$(addprefix -I, $(dir $(DEPS)))
+_LIBPATHS=$(addprefix -l, $(patsubst lib%.a,%,$(notdir $(DEPS))))
+
+CFLAGS += -Wall -Werror -Wextra -Wno-unused-parameter -Wno-missing-field-initializers
+CFLAGS += -g
+CFLAGS += -MMD -MP -MF ${_DEPSDIR}/$(subst /,-,$*).d
+
+.PHONY: clean clean-gen clean-bin clean-obj clean-misc clean-backups
+.PHONY: all
+
+all: subdirs $(BIN) $(LIB) $(EXTRA)
+
+$(LIB): $(OBJ) $(DEPS)
+ $(AR) -rcs $@ $(OBJ)
+
+$(BIN): $(OBJ) $(EXTRADEP) $(DEPS)
+ $(CC) -o $@ $(OBJ) $(_LIBSRCHPATHS) $(_LIBPATHS)
+
+$(DEPS):
+ @cd $(dir $@) && $(MAKE)
+
+subdirs:
+ @for i in $(SUB); do (\
+ cd $$i && \
+ $(MAKE) || \
+ exit 1 \
+ ) || exit 1; done
+
+subdirs-clean:
+ @for i in $(SUB); do (\
+ cd $$i && \
+ $(MAKE) clean|| \
+ exit 1 \
+ ); done
+
+subdirs-install:
+ @for i in $(SUB); do (\
+ cd $$i && \
+ $(MAKE) install|| \
+ exit 1 \
+ ); done
+
+
+clean: subdirs-clean
+ rm -f ${BIN} ${OBJ} ${CLEAN}
+
+
+install: subdirs-install $(INSTBIN) $(INSTLIB) $(INSTHDR) $(INSTPKG)
+ @if [ ! -z "$(INSTBIN)" ]; then \
+ echo install $(abspath $(INSTBIN) $(DESTDIR)/$(INST_ROOT)/bin); \
+ mkdir -p $(abspath $(DESTDIR)/$(INST_ROOT)/bin); \
+ install $(INSTBIN) $(abspath $(DESTDIR)/$(INST_ROOT)/bin); \
+ fi
+ @if [ ! -z "$(INSTLIB)" ]; then \
+ echo install -m 644 $(INSTLIB) $(abspath $(DESTDIR)/$(INST_ROOT)/lib); \
+ mkdir -p $(abspath $(DESTDIR)/$(INST_ROOT)/lib); \
+ install -m 644 $(INSTLIB) $(abspath $(DESTDIR)/$(INST_ROOT)/lib); \
+ fi
+ @if [ ! -z "$(INSTHDR)" ]; then \
+ echo install $(INSTHDR) $(abspath $(DESTDIR)/$(INST_ROOT)/include); \
+ mkdir -p $(abspath $(DESTDIR)/$(INST_ROOT)/include); \
+ install $(INSTHDR) $(abspath $(DESTDIR)/$(INST_ROOT)/include); \
+ fi
+ @if [ ! -z "$(INSTPKG)" ]; then \
+ echo install $(abspath $(INSTPKG) $(DESTDIR)/$(INST_ROOT)/lib/pkgconfig); \
+ mkdir -p $(abspath $(DESTDIR)/$(INST_ROOT)/lib/pkgconfig); \
+ install $(abspath $(INSTPKG) $(DESTDIR)/$(INST_ROOT)/lib/pkgconfig); \
+ fi
+
+subdirs-uninstall:
+ @for i in $(SUB); do (\
+ cd $$i && \
+ $(MAKE) uninstall|| \
+ exit 1 \
+ ); done
+
+uninstall: subdirs-uninstall
+ @for i in $(INSTBIN); do \
+ echo rm -f $(abspath $(DESTDIR)/$(INST_ROOT)/bin/$$i); \
+ rm -f $(abspath $(DESTDIR)/$(INST_ROOT)/bin/$$i); \
+ done
+ @for i in $(INSTLIB); do \
+ echo rm -f $(abspath $(DESTDIR)/$(INST_ROOT)/lib/$$i); \
+ rm -f $(abspath $(DESTDIR)/$(INST_ROOT)/lib/$$i); \
+ done
+ @for i in $(INSTHDR); do \
+ echo rm -f $(abspath $(DESTDIR)/$(INST_ROOT)/include/$$i); \
+ rm -f $(abspath $(DESTDIR)/$(INST_ROOT)/include/$$i); \
+ done
+ @for i in $(INSTPKG); do \
+ echo rm -f $(abspath $(DESTDIR)/$(INST_ROOT)/lib/pkgconfig/$$i); \
+ rm -f $(abspath $(DESTDIR)/$(INST_ROOT)/lib/pkgconfig/$$i); \
+ done
+
+clean-backups:
+ find ./ -name .*.sw* -exec rm -f {} \;
+ find ./ -name *.bak -exec rm -f {} \;
+
+%.o: %.c $(GENHDR) .deps
+ $(CC) -c $(CFLAGS) $(_LIBINCPATHS) $<
+
+.deps:
+ mkdir -p $(_DEPSDIR)
+
+config.mk: configure
+ ./configure
+
+-include $(_DEPS)
--- /dev/null
+++ b/mk/lexyacc.mk
@@ -1,0 +1,7 @@
+NECFLAGS = $(subst -Werror,,$(subst -Wall,,$(CFLAGS)))
+
+%.h %.c: %.y
+ yacc -d -o$*.c $<
+
+%.c: %.l
+ flex -o$*.c $<
--- /dev/null
+++ b/muse/Makefile
@@ -1,0 +1,7 @@
+INSTBIN=muse
+BIN=muse
+OBJ=muse.o
+
+DEPS=../parse/libparse.a
+
+include ../mk/c.mk
--- /dev/null
+++ b/muse/muse.c
@@ -1,0 +1,150 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+
+#include "../config.h"
+
+/* FIXME: move into one place...? */
+Node *file;
+char *outfile;
+int merge;
+int debug;
+char debugopt[128];
+char **incpaths;
+size_t nincpaths;
+
+static void usage(char *prog)
+{
+ printf("%s [-hIdos] [-o outfile] [-m] inputs\n", prog);
+ printf("\t-h\tprint this help\n");
+ printf("\t-m\ttreat the inputs as usefiles and merge them into outfile\n");
+ printf("\t\tThe outfile must be the same name as each package merged.\n");
+ printf("\t-I path\tAdd 'path' to use search path\n");
+ printf("\t-d\tPrint debug dumps\n");
+ printf("\t-o out\tOutput to outfile\n");
+ printf("\t-s\tShow the contents of usefiles `inputs`\n");
+}
+
+static void dumpuse(char *path)
+{
+ Stab *globls;
+ FILE *f;
+
+ globls = file->file.globls;
+ f = fopen(path, "r");
+ loaduse(f, globls);
+ fclose(f);
+ dumpstab(globls, stdout);
+}
+
+static void genuse(char *path)
+{
+ Stab *globls;
+ char *p;
+ FILE *f;
+ char buf[1024];
+
+ globls = file->file.globls;
+ tyinit(globls);
+ tokinit(path);
+ yyparse();
+
+ infer(file);
+ if (outfile) {
+ p = outfile;
+ } else {
+ swapsuffix(buf, sizeof buf, path, ".myr", ".use");
+ p = buf;
+ }
+ f = fopen(p, "w");
+ writeuse(f, file);
+ fclose(f);
+}
+
+static void mergeuse(char *path)
+{
+ FILE *f;
+ Stab *st;
+
+ st = file->file.exports;
+ f = fopen(path, "r");
+ if (!f)
+ die("Couldn't open %s\n", path);
+ loaduse(f, st);
+ fclose(f);
+}
+
+int main(int argc, char **argv)
+{
+ FILE *f;
+ int opt;
+ int i;
+
+ while ((opt = getopt(argc, argv, "d::hmo:I:")) != -1) {
+ switch (opt) {
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ break;
+ case 'm':
+ merge = 1;
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ case 'd':
+ debug = 1;
+ while (optarg && *optarg)
+ debugopt[*optarg++ & 0x7f] = 1;
+ break;
+ case 'I':
+ lappend(&incpaths, &nincpaths, optarg);
+ break;
+ default:
+ usage(argv[0]);
+ exit(0);
+ break;
+ }
+ }
+
+ lappend(&incpaths, &nincpaths, Instroot "/lib/myr");
+ if (merge) {
+ if (!outfile) {
+ fprintf(stderr, "Output file needed when merging usefiles.");
+ exit(1);
+ }
+
+ file = mkfile("internal");
+ file->file.exports = mkstab();
+ file->file.globls = mkstab();
+ updatens(file->file.exports, outfile);
+ tyinit(file->file.globls);
+ for (i = optind; i < argc; i++)
+ mergeuse(argv[i]);
+ infer(file);
+ f = fopen(outfile, "w");
+ writeuse(f, file);
+ fclose(f);
+ } else {
+ for (i = optind; i < argc; i++) {
+ file = mkfile(argv[i]);
+ file->file.exports = mkstab();
+ file->file.globls = mkstab();
+ if (debugopt['s'])
+ dumpuse(argv[i]);
+ else
+ genuse(argv[i]);
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+++ b/myrbuild/Makefile
@@ -1,0 +1,8 @@
+INSTBIN=myrbuild
+BIN=myrbuild
+OBJ=myrbuild.o
+
+DEPS=../parse/libparse.a
+
+include ../config.mk
+include ../mk/c.mk
--- /dev/null
+++ b/myrbuild/myrbuild.c
@@ -1,0 +1,495 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/utsname.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <regex.h>
+#include <err.h>
+
+#include "parse.h"
+
+#include "../config.h"
+
+/* make libparse happy */
+Node *file;
+char *filename;
+
+/* options to pass along to the compiler */
+int genasm = 0;
+
+/* binaries we call out to */
+char *mc = "6m";
+char *as = "as";
+char *ar = "ar";
+char *ld = "ld";
+char *muse = "muse";
+/* the name of the output file */
+char *libname;
+char *binname;
+/* additional paths to search for packages */
+char **incpaths;
+size_t nincpaths;
+/* libraries to link against, and their deps */
+Htab *libgraph; /* string -> null terminated string list */
+/* the linker script to use */
+char *ldscript;
+
+char *sysname;
+
+regex_t usepat;
+Htab *compiled; /* used as string set */
+Htab *loopdetect; /* used as string set */
+
+static void usage(char *prog)
+{
+ printf("%s [-h] [-I path] [-l lib] [-b bin] inputs...\n", prog);
+ printf("\t-h\tprint this help\n");
+ printf("\t-b bin\tBuild a binary called 'bin'\n");
+ printf("\t-l lib\tBuild a library called 'name'\n");
+ printf("\t-s script\tUse the linker script 'script' when linking\n");
+ printf("\t-I path\tAdd 'path' to use search path\n");
+ printf("\t-S\tGenerate assembly files for all compiled code\n");
+}
+
+int hassuffix(char *path, char *suffix)
+{
+ int pathlen;
+ int sufflen;
+
+ pathlen = strlen(path);
+ sufflen = strlen(suffix);
+
+ if (sufflen > pathlen)
+ return 0;
+ return !strcmp(&path[pathlen-sufflen], suffix);
+}
+
+int isquoted(char *path)
+{
+ return path[0] == '"' && path[strlen(path) - 1] == '"';
+}
+
+char *usetomyr(char *path)
+{
+ char buf[1024];
+ /* skip initial quote */
+ path++;
+ if (!hassuffix(path, ".use\"")) {
+ fprintf(stderr, "\"%s, should end with \".use\"\n", path);
+ exit(1);
+ }
+ swapsuffix(buf, 1024, path, ".use\"", ".myr");
+ return strdup(buf);
+}
+
+void printl(char **lst)
+{
+ printf("\t");
+ printf("%s\t", *lst++);
+ while (*lst)
+ printf("%s ", *lst++);
+ printf("\n");
+}
+
+void gencmd(char ***cmd, size_t *ncmd, char *bin, char *file, char **extra, size_t nextra)
+{
+ size_t i;
+
+ *cmd = NULL;
+ *ncmd = 0;
+ lappend(cmd, ncmd, bin);
+ for (i = 0; i < nincpaths; i++) {
+ lappend(cmd, ncmd, "-I");
+ lappend(cmd, ncmd, incpaths[i]);
+ }
+ for (i = 0; i < nextra; i++)
+ lappend(cmd, ncmd, extra[i]);
+ lappend(cmd, ncmd, file);
+ lappend(cmd, ncmd, NULL);
+}
+
+void run(char **cmd)
+{
+ pid_t pid;
+ int status;
+
+ printl(cmd);
+ pid = fork();
+ status = 0;
+ if (pid == -1) {
+ err(1, "Could not fork");
+ } else if (pid == 0) {
+ if (execvp(cmd[0], cmd) == -1)
+ err(1, "Failed to exec %s", cmd[0]);
+ } else {
+ waitpid(pid, &status, 0);
+ }
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
+ exit(WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ die("%s: exited with signal %d\n", cmd[0], WTERMSIG(status));
+}
+
+int isfresh(char *from, char *to)
+{
+ struct stat from_sb, to_sb;
+
+ if (stat(from, &from_sb))
+ err(1, "Could not find %s", from);
+ if (stat(to, &to_sb) == -1)
+ return 0;
+
+ return from_sb.st_mtime <= to_sb.st_mtime;
+}
+
+int inlist(char **list, size_t sz, char *str)
+{
+ size_t i;
+
+ for (i = 0; i < sz; i++)
+ if (!strcmp(list[i], str))
+ return 1;
+ return 0;
+}
+
+void getdeps(char *file, char **deps, size_t depsz, size_t *ndeps)
+{
+ char buf[2048];
+
+ regmatch_t m[2];
+ size_t i;
+ FILE *f;
+ char *dep;
+
+ f = fopen(file, "r");
+ if (!f)
+ err(1, "Could not open file \"%s\"", file);
+
+ i = 0;
+ while (fgets(buf, sizeof buf, f)) {
+ if (regexec(&usepat, buf, 2, m, 0) == REG_NOMATCH)
+ continue;
+ if (i == depsz)
+ die("Too many deps for file %s", file);
+ dep = strdupn(&buf[m[1].rm_so], m[1].rm_eo - m[1].rm_so);
+ if (!inlist(deps, i, dep))
+ deps[i++] = dep;
+ else
+ free(dep);
+ }
+ fclose(f);
+ *ndeps = i;
+}
+
+FILE *openlib(char *lib)
+{
+ FILE *f;
+ char buf[1024];
+ size_t i;
+
+ snprintf(buf, sizeof buf, "%s/%s/%s", Instroot, "/lib/myr", lib);
+ f = fopen(buf, "r");
+ if (f)
+ return f;
+ for (i = 0; i < nincpaths; i++) {
+ snprintf(buf, sizeof buf, "%s/%s", incpaths[i], lib);
+ f = fopen(buf, "r");
+ if (f)
+ return f;
+ }
+ err(1, "could not open library file %s\n", lib);
+}
+
+void scrapelib(Htab *g, char *lib)
+{
+ char **deps;
+ size_t ndeps;
+ FILE *use;
+ char *l;
+
+ if (hthas(libgraph, lib))
+ return;
+ deps = NULL;
+ ndeps = 0;
+ use = openlib(lib);
+ if (fgetc(use) != 'U')
+ err(1, "library \"%s\" is not a usefile.", lib);
+ /* we don't care about the usefile's name */
+ free(rdstr(use));
+ while (fgetc(use) == 'L') {
+ l = rdstr(use);
+ lappend(&deps, &ndeps, l);
+ scrapelib(g, l);
+ }
+ lappend(&deps, &ndeps, NULL);
+ htput(g, lib, deps);
+}
+
+void compile(char *file)
+{
+ size_t i, ndeps;
+ char **cmd;
+ size_t ncmd;
+ char *s;
+ char *localdep;
+ char *deps[512];
+ char use[1024];
+ char obj[1024];
+ char *extra[32];
+ size_t nextra = 0;
+
+ if (hthas(compiled, file))
+ return;
+ if (hthas(loopdetect, file))
+ die("Cycle in dependency graph, involving %s\n", file);
+ htput(loopdetect, file, file);
+ if (hassuffix(file, ".myr")) {
+ swapsuffix(use, sizeof use, file, ".myr", ".use");
+ swapsuffix(obj, sizeof obj, file, ".myr", ".o");
+ getdeps(file, deps, 512, &ndeps);
+ for (i = 0; i < ndeps; i++) {
+ if (isquoted(deps[i])) {
+ localdep = usetomyr(deps[i]);
+ compile(localdep);
+ free(localdep);
+ } else {
+ scrapelib(libgraph, deps[i]);
+ }
+ }
+ if (isfresh(file, use))
+ goto done;
+ if (isfresh(file, obj))
+ goto done;
+ if (genasm)
+ extra[nextra++] = "-S";
+ gencmd(&cmd, &ncmd, mc, file, extra, nextra);
+ run(cmd);
+ } else if (hassuffix(file, ".s")) {
+ swapsuffix(obj, sizeof obj, file, ".s", ".o");
+ if (isfresh(file, obj))
+ goto done;
+ extra[nextra++] = "-g";
+ extra[nextra++] = "-o";
+ extra[nextra++] = obj;
+ gencmd(&cmd, &ncmd, as, file, extra, nextra);
+ run(cmd);
+ }
+done:
+ s = strdup(file);
+ htput(compiled, s, s);
+ htdel(loopdetect, file);
+}
+
+void mergeuse(char **files, size_t nfiles)
+{
+ char **args;
+ size_t i, nargs;
+ char buf[1024];
+
+ args = NULL;
+ nargs = 0;
+ lappend(&args, &nargs, strdup(muse));
+ lappend(&args, &nargs, strdup("-mo"));
+ lappend(&args, &nargs, strdup(libname));
+ for (i = 0; i < nfiles; i++) {
+ if (hassuffix(files[i], ".myr")) {
+ swapsuffix(buf, sizeof buf, files[i], ".myr", ".use");
+ lappend(&args, &nargs, strdup(buf));
+ } else if (!hassuffix(files[i], ".s")) {
+ die("Unknown file type %s", files[i]);
+ }
+ }
+ lappend(&args, &nargs, NULL);
+
+ run(args);
+
+ for (i = 0; i < nargs; i++)
+ free(args[i]);
+ lfree(&args, &nargs);
+}
+
+void archive(char **files, size_t nfiles)
+{
+ char **args;
+ size_t i, nargs;
+ char buf[1024];
+
+ args = NULL;
+ nargs = 0;
+ snprintf(buf, sizeof buf, "lib%s.a", libname);
+ lappend(&args, &nargs, strdup(ar));
+ lappend(&args, &nargs, strdup("-rcs"));
+ lappend(&args, &nargs, strdup(buf));
+ for (i = 0; i < nfiles; i++) {
+ if (hassuffix(files[i], ".myr"))
+ swapsuffix(buf, sizeof buf, files[i], ".myr", ".o");
+ else if (hassuffix(files[i], ".s"))
+ swapsuffix(buf, sizeof buf, files[i], ".s", ".o");
+ else
+ die("Unknown file type %s", files[i]);
+ lappend(&args, &nargs, strdup(buf));
+ }
+ lappend(&args, &nargs, NULL);
+
+ run(args);
+
+ for (i = 0; i < nargs; i++)
+ free(args[i]);
+ lfree(&args, &nargs);
+}
+
+void visit(char ***args, size_t *nargs, size_t head, Htab *g, char *n, Htab *looped, Htab *marked)
+{
+ char **deps;
+ char buf[1024];
+
+ if (hthas(looped, n))
+ err(1, "cycle in library dependency graph involving %s\n", n);
+ if (hthas(marked, n))
+ return;
+ htput(looped, n, n);
+ for (deps = htget(g, n); *deps; deps++)
+ visit(args, nargs, head, g, *deps, looped, marked);
+ htdel(looped, n);
+ htput(marked, n, n);
+ snprintf(buf, sizeof buf, "-l%s", n);
+ linsert(args, nargs, head, strdup(buf));
+}
+
+/* topologically sorts the dependency graph of the libraries. */
+void addlibs(char ***args, size_t *nargs, Htab *g)
+{
+ void **libs;
+ size_t nlibs;
+ size_t i;
+ size_t head;
+ Htab *looped;
+ Htab *marked;
+
+ libs = htkeys(g, &nlibs);
+ looped = mkht(strhash, streq);
+ marked = mkht(strhash, streq);
+ head = *nargs;
+ for (i = 0; i < nlibs; i++)
+ visit(args, nargs, head, g, libs[i], looped, marked);
+}
+
+void linkobj(char **files, size_t nfiles)
+{
+ char **args;
+ size_t i, nargs;
+ char buf[1024];
+
+ if (!binname)
+ binname = "a.out";
+
+ args = NULL;
+ nargs = 0;
+
+ /* ld -T ldscript -o outfile */
+ lappend(&args, &nargs, strdup(ld));
+ lappend(&args, &nargs, strdup("-o"));
+ lappend(&args, &nargs, strdup(binname));
+
+ /* ld -T ldscript */
+ if (ldscript) {
+ snprintf(buf, sizeof buf, "-T%s", ldscript);
+ lappend(&args, &nargs, strdup(buf));
+ }
+
+ /* ld -T ldscript -o outfile foo.o bar.o baz.o */
+ for (i = 0; i < nfiles; i++) {
+ if (hassuffix(files[i], ".myr"))
+ swapsuffix(buf, sizeof buf, files[i], ".myr", ".o");
+ else if (hassuffix(files[i], ".s"))
+ swapsuffix(buf, sizeof buf, files[i], ".s", ".o");
+ else
+ die("Unknown file type %s", files[i]);
+ lappend(&args, &nargs, strdup(buf));
+ }
+
+ /* ld -T ldscript -o outfile foo.o bar.o baz.o -L/path1 -L/path2 */
+ for (i = 0; i < nincpaths; i++) {
+ snprintf(buf, sizeof buf, "-L%s", incpaths[i]);
+ lappend(&args, &nargs, strdup(buf));
+ }
+ snprintf(buf, sizeof buf, "-L%s%s", Instroot, "/lib/myr");
+ lappend(&args, &nargs, strdup(buf));
+
+ /* ld -T ldscript -o outfile foo.o bar.o baz.o -L/path1 -L/path2 -llib1 -llib2*/
+ addlibs(&args, &nargs, libgraph);
+
+ /* OSX wants a minimum version specified to prevent warnings*/
+ if (!strcmp(sysname, "Darwin")) {
+ lappend(&args, &nargs, strdup("-macosx_version_min"));
+ lappend(&args, &nargs, strdup("10.6"));
+ }
+
+ /* the null terminator for exec() */
+ lappend(&args, &nargs, NULL);
+
+ run(args);
+
+ for (i = 0; i < nargs; i++)
+ free(args[i]);
+ lfree(&args, &nargs);
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ int i;
+ struct utsname name;
+
+ if (uname(&name) == 0)
+ sysname = strdup(name.sysname);
+ while ((opt = getopt(argc, argv, "hb:l:s:SI:C:A:M:L:R:")) != -1) {
+ switch (opt) {
+ case 'b': binname = optarg; break;
+ case 'l': libname = optarg; break;
+ case 's': ldscript = optarg; break;
+ case 'S': genasm = 1; break;
+ case 'C': mc = optarg; break;
+ case 'A': as = optarg; break;
+ case 'M': muse = optarg; break;
+ case 'L': ld = optarg; break;
+ case 'R': ar = optarg; break;
+ case 'I':
+ lappend(&incpaths, &nincpaths, optarg);
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ break;
+ default:
+ usage(argv[0]);
+ exit(0);
+ break;
+ }
+ }
+
+ if (libname && binname)
+ die("Can't specify both library and binary names");
+
+ libgraph = mkht(strhash, streq);
+ compiled = mkht(strhash, streq);
+ loopdetect = mkht(strhash, streq);
+ regcomp(&usepat, "^[[:space:]]*use[[:space:]]+([^[:space:]]+)", REG_EXTENDED);
+ for (i = optind; i < argc; i++)
+ compile(argv[i]);
+ if (libname) {
+ mergeuse(&argv[optind], argc - optind);
+ archive(&argv[optind], argc - optind);
+ } else {
+ linkobj(&argv[optind], argc - optind);
+ }
+
+ return 0;
+}
--- /dev/null
+++ b/myrtypes/Makefile
@@ -1,0 +1,7 @@
+INSTBIN=myrtypes
+BIN=myrtypes
+OBJ=myrtypes.o
+
+DEPS=../parse/libparse.a
+
+include ../mk/c.mk
--- /dev/null
+++ b/myrtypes/myrtypes.c
@@ -1,0 +1,222 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+
+#include "../config.h"
+
+/* FIXME: move into one place...? */
+Node *file;
+int fromuse;
+int debug;
+char debugopt[128];
+char **incpaths;
+size_t nincpaths;
+
+static void printindent(int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ printf(" ");
+}
+
+static void dumptypes(Node *n, int indent)
+{
+ size_t i;
+ char *ty;
+
+ if (!n)
+ return;
+ switch (n->type) {
+ case Nfile:
+ for (i = 0; i < n->file.nuses; i++)
+ dumptypes(n->file.uses[i], indent);
+ for (i = 0; i < n->file.nstmts; i++)
+ dumptypes(n->file.stmts[i], indent);
+ break;
+ case Ndecl:
+ printindent(indent);
+ if (n->decl.isconst)
+ printf("const ");
+ else
+ printf("var ");
+ ty = tystr(n->decl.type);
+ printf("%s : %s\n", namestr(n->decl.name), ty);
+ free(ty);
+ dumptypes(n->decl.init, indent + 1);
+ break;
+ case Nblock:
+ for (i = 0; i < n->block.nstmts; i++)
+ dumptypes(n->block.stmts[i], indent);
+ break;
+ case Nifstmt:
+ dumptypes(n->ifstmt.cond, indent);
+ dumptypes(n->ifstmt.iftrue, indent);
+ dumptypes(n->ifstmt.iffalse, indent);
+ break;
+ case Nloopstmt:
+ dumptypes(n->loopstmt.init, indent);
+ dumptypes(n->loopstmt.cond, indent);
+ dumptypes(n->loopstmt.step, indent);
+ dumptypes(n->loopstmt.body, indent);
+ break;
+ case Niterstmt:
+ dumptypes(n->iterstmt.elt, indent);
+ dumptypes(n->iterstmt.seq, indent);
+ dumptypes(n->iterstmt.body, indent);
+ break;
+ case Nmatchstmt:
+ dumptypes(n->matchstmt.val, indent);
+ for (i = 0; i < n->matchstmt.nmatches; i++)
+ dumptypes(n->matchstmt.matches[i], indent);
+ break;
+ case Nmatch:
+ dumptypes(n->match.pat, indent);
+ dumptypes(n->match.block, indent);
+ break;
+ case Nuse:
+ printindent(indent);
+ if (n->use.islocal)
+ printf("Use \"%s\"\n", n->use.name);
+ else
+ printf("Use %s\n", n->use.name);
+ break;
+ case Nexpr:
+ dumptypes(n->expr.idx, indent);
+ for (i = 0; i < n->expr.nargs; i++)
+ dumptypes(n->expr.args[i], indent);
+ break;
+ case Nlit:
+ switch (n->lit.littype) {
+ case Lfunc: dumptypes(n->lit.fnval, indent); break;
+ default: break;
+ }
+ break;
+ case Nfunc:
+ printindent(indent);
+ printf("Args:\n");
+ for (i = 0; i < n->func.nargs; i++)
+ dumptypes(n->func.args[i], indent+1);
+ printindent(indent);
+ printf("Body:\n");
+ dumptypes(n->func.body, indent + 1);
+ break;
+ case Nimpl:
+ die("Ntrait/Nimpl not yet supported!");
+ break;
+ case Nname:
+ break;
+ case Nnone:
+ die("Nnone not a real node type!");
+ break;
+ }
+}
+
+void dumpucon(Ucon *uc, int indent)
+{
+ printindent(indent);
+ printf("`");
+ if (uc->name->name.ns)
+ printf("%s.", uc->name->name.ns);
+ printf("%s\n", uc->name->name.name);
+}
+
+void dumpsyms(Stab *st, int indent)
+{
+ size_t i, n;
+ void **k;
+
+ /* decls */
+ k = htkeys(st->dcl, &n);
+ for (i = 0; i < n; i++) {
+ dumptypes(getdcl(st, k[i]), indent);
+ }
+ free(k);
+
+ /* union constructors */
+ k = htkeys(st->uc, &n);
+ for (i = 0; i < n; i++)
+ dumpucon(getucon(st, k[i]), indent + 1);
+
+
+ /* sub-namespaces */
+ k = htkeys(st->ns, &n);
+ for (i = 0; i < n; i++) {
+ printindent(indent + 1);
+ printf("namespace %s:\n", (char*)k[i]);
+ dumpsyms(getns_str(st, k[i]), indent + 2);
+ }
+
+ free(k);
+}
+
+static void usage(char *prog)
+{
+ printf("%s [-hu] [-d opt][-I path] inputs\n", prog);
+ printf("\t-h\tprint this help\n");
+ printf("\t-I path\tAdd 'path' to use search path\n");
+ printf("\t-d\tPrint debug dumps\n");
+ printf("\t-u\tLoad the symbols to dump from a use file\n");
+}
+
+int main(int argc, char **argv)
+{
+ FILE *f;
+ int opt;
+ int i;
+
+ while ((opt = getopt(argc, argv, "hud:I:")) != -1) {
+ switch (opt) {
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ break;
+ case 'u':
+ fromuse = 1;
+ break;
+ case 'd':
+ debug = 1;
+ while (optarg && *optarg)
+ debugopt[*optarg++ & 0x7f] = 1;
+ break;
+ case 'I':
+ lappend(&incpaths, &nincpaths, optarg);
+ break;
+ default:
+ usage(argv[0]);
+ exit(0);
+ break;
+ }
+ }
+
+ for (i = optind; i < argc; i++) {
+ lappend(&incpaths, &nincpaths, Instroot "/lib/myr");
+ file = mkfile(argv[i]);
+ file->file.exports = mkstab();
+ file->file.globls = mkstab();
+ tyinit(file->file.globls);
+ printf("%s:\n", argv[i]);
+ if (fromuse) {
+ f = fopen(argv[i], "r");
+ if (!f)
+ die("Unable to open usefile %s\n", argv[i]);
+ loaduse(f, file->file.globls);
+ dumpsyms(file->file.globls, 1);
+ } else {
+ tokinit(argv[i]);
+ yyparse();
+ infer(file);
+ dumpsyms(file->file.globls, 1);
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+++ b/parse/Makefile
@@ -1,0 +1,20 @@
+LIB=libparse.a
+OBJ=bitset.o \
+ dump.o \
+ gram.o \
+ htab.o \
+ infer.o \
+ names.o \
+ node.o \
+ specialize.o \
+ stab.o \
+ tok.o \
+ type.o \
+ use.o \
+ util.o
+
+GENHDR=gram.h
+CLEAN=gram.c gram.h
+
+include ../mk/lexyacc.mk
+include ../mk/c.mk
--- /dev/null
+++ b/parse/bitset.c
@@ -1,0 +1,210 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+
+#include "parse.h"
+
+#define Sizetbits (CHAR_BIT*sizeof(size_t)) /* used in graph reprs */
+
+/* Equalizes the size of a and b by
+ * growing the smaller to the size of the
+ * larger, zeroing out the new elements.
+ * This allows the code to simply iterate
+ * over both without keeping track of the
+ * minimum size.
+ */
+static void eqsz(Bitset *a, Bitset *b)
+{
+ size_t sz;
+ size_t i;
+ size_t *p;
+
+ if (a->nchunks > b->nchunks)
+ sz = a->nchunks;
+ else
+ sz = b->nchunks;
+
+ p = zalloc(sz * sizeof(size_t));
+ for (i = 0; i < a->nchunks; i++)
+ p[i] = a->chunks[i];
+ free(a->chunks);
+ a->chunks = p;
+ a->nchunks = sz;
+
+ p = zalloc(sz * sizeof(size_t));
+ for (i = 0; i < b->nchunks; i++)
+ p[i] = b->chunks[i];
+ free(b->chunks);
+ b->chunks = p;
+ b->nchunks = sz;
+}
+
+/* Creates a new all-zero bit set */
+Bitset *mkbs()
+{
+ Bitset *bs;
+
+ bs = xalloc(sizeof(Bitset));
+ bs->nchunks = 1;
+ bs->chunks = zalloc(1*sizeof(size_t));
+ return bs;
+}
+
+/* Frees a bitset. Safe to call on NULL. */
+void bsfree(Bitset *bs)
+{
+ if (!bs)
+ return;
+ free(bs->chunks);
+ free(bs);
+}
+
+/* Duplicates a bitset. NULL is duplicated to NULL. */
+Bitset *bsdup(Bitset *a)
+{
+ Bitset *bs;
+
+ if (!a)
+ return NULL;
+ bs = xalloc(sizeof(Bitset));
+ bs->nchunks = a->nchunks;
+ bs->chunks = xalloc(a->nchunks*sizeof(size_t));
+ memcpy(bs->chunks, a->chunks, a->nchunks*sizeof(size_t));
+ return bs;
+}
+
+/* Zeroes all values in a bit set */
+Bitset *bsclear(Bitset *bs)
+{
+ size_t i;
+
+ if (!bs)
+ return mkbs();
+ for (i = 0; i < bs->nchunks; i++)
+ bs->chunks[i] = 0;
+ return bs;
+}
+
+/* Counts the number of values held in a bit set */
+size_t bscount(Bitset *bs)
+{
+ size_t i, j, n;
+
+ n = 0;
+ for (i = 0; i < bs->nchunks; i++)
+ for (j = 0; j < sizeof(size_t)*CHAR_BIT; j++)
+ if (bs->chunks[i] & 1ULL << j)
+ n++;
+ return n;
+}
+
+/* A slightly tricky function to iterate over the contents
+ * of a bitset. It returns true immediately if 'elt' is in
+ * the bitset, otherwise it seeks forward to the next value
+ * held in the bitset and stores it in elt. If there are no
+ * more values, it returns false to stop iteration. Note,
+ * this means that you need to increment elt every time you
+ * pass through.
+ *
+ * Typical usage of this function:
+ *
+ * for (i = 0; bsiter(set, &i); i++)
+ * use(i);
+ *
+ * The increment of 'i' in the for loop is needed in order
+ * to prevent the function from returning the same value
+ * repeatedly.
+ */
+int bsiter(Bitset *bs, size_t *elt)
+{
+ size_t i;
+
+ for (i = *elt; i < bsmax(bs); i++) {
+ while (i < bsmax(bs) && !bs->chunks[i/Sizetbits])
+ i = (i + Sizetbits) & ~(Sizetbits - 1);
+ if (bshas(bs, i)) {
+ *elt = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Returns the largest value that the bitset can possibly
+ * hold. It's conservative, but scanning the entire bitset
+ * is a bit slow. This is mostly an aid to iterate over it. */
+size_t bsmax(Bitset *bs)
+{
+ return bs->nchunks*Sizetbits;
+}
+
+void bsput(Bitset *bs, size_t elt)
+{
+ size_t sz;
+ if (elt >= bs->nchunks*Sizetbits) {
+ sz = (elt/Sizetbits)+1;
+ bs->chunks = zrealloc(bs->chunks, bs->nchunks*sizeof(size_t), sz*sizeof(size_t));
+ bs->nchunks = sz;
+ }
+ bs->chunks[elt/Sizetbits] |= 1ULL << (elt % Sizetbits);
+}
+
+void bsdel(Bitset *bs, size_t elt)
+{
+ if (elt < bs->nchunks*Sizetbits)
+ bs->chunks[elt/Sizetbits] &= ~(1ULL << (elt % Sizetbits));
+}
+
+
+void bsunion(Bitset *a, Bitset *b)
+{
+ size_t i;
+
+ eqsz(a, b);
+ for (i = 0; i < a->nchunks; i++)
+ a->chunks[i] |= b->chunks[i];
+}
+
+void bsintersect(Bitset *a, Bitset *b)
+{
+ size_t i;
+
+ eqsz(a, b);
+ for (i = 0; i < a->nchunks; i++)
+ a->chunks[i] &= b->chunks[i];
+}
+
+void bsdiff(Bitset *a, Bitset *b)
+{
+ size_t i;
+
+ eqsz(a, b);
+ for (i = 0; i < a->nchunks; i++)
+ a->chunks[i] &= ~b->chunks[i];
+}
+
+int bseq(Bitset *a, Bitset *b)
+{
+ size_t i;
+
+ eqsz(a, b);
+ for (i = 0; i < a->nchunks; i++) {
+ if (a->chunks[i] != b->chunks[i])
+ return 0;
+ }
+ return 1;
+}
+
+int bsissubset(Bitset *set, Bitset *sub)
+{
+ size_t i;
+
+ eqsz(set, sub);
+ for (i = 0; i < set->nchunks; i++)
+ if ((sub->chunks[i] & set->chunks[i]) != set->chunks[i])
+ return 0;
+ return 1;
+}
--- /dev/null
+++ b/parse/dump.c
@@ -1,0 +1,251 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+
+static void indent(FILE *fd, int depth)
+{
+ int i;
+ for (i = 0; i < depth; i++)
+ fprintf(fd, " ");
+}
+
+/* outputs a fully qualified name */
+static void outname(Node *n, FILE *fd)
+{
+ if (n->name.ns)
+ fprintf(fd, "%s.", n->name.ns);
+ fprintf(fd, "%s", n->name.name);
+}
+
+/* outputs a sym in a one-line short form (ie,
+ * the initializer is not printed, and the node is not
+ * expressed in indented tree. */
+static void outsym(Node *s, FILE *fd, int depth)
+{
+ char buf[1024];
+
+ indent(fd, depth);
+ if (s->decl.isconst)
+ fprintf(fd, "const ");
+ else
+ fprintf(fd, "var ");
+ outname(s->decl.name, fd);
+ fprintf(fd, " : %s\n", tyfmt(buf, 1024, s->decl.type));
+}
+
+void dumpsym(Node *s, FILE *fd)
+{
+ outsym(s, fd, 0);
+}
+
+/* Outputs a symbol table, and it's sub-tables
+ * recursively, with a sigil describing the symbol
+ * type, as follows:
+ * T type
+ * S symbol
+ * N namespace
+ *
+ * Does not print captured variables.
+ */
+static void outstab(Stab *st, FILE *fd, int depth)
+{
+ size_t i, n;
+ void **k;
+ char *ty;
+ Type *t;
+
+ indent(fd, depth);
+ fprintf(fd, "Stab %p (super = %p, name=\"%s\")\n", st, st->super, namestr(st->name));
+ if (!st)
+ return;
+
+ /* print types */
+ k = htkeys(st->ty, &n);
+ for (i = 0; i < n; i++) {
+ indent(fd, depth + 1);
+ fprintf(fd, "T ");
+ /* already indented */
+ outname(k[i], fd);
+ t = gettype(st, k[i]);
+ ty = tystr(t);
+ fprintf(fd, " = %s [tid=%d]\n", ty, t->tid);
+ free(ty);
+ }
+ free(k);
+
+ /* dump declarations */
+ k = htkeys(st->dcl, &n);
+ for (i = 0; i < n; i++) {
+ indent(fd, depth + 1);
+ fprintf(fd, "S ");
+ /* already indented */
+ outsym(getdcl(st, k[i]), fd, 0);
+ }
+ free(k);
+
+ /* dump sub-namespaces */
+ k = htkeys(st->ns, &n);
+ for (i = 0; i < n; i++) {
+ indent(fd, depth + 1);
+ fprintf(fd, "N %s\n", (char*)k[i]);
+ outstab(getns_str(st, k[i]), fd, depth + 1);
+ }
+ free(k);
+}
+
+void dumpstab(Stab *st, FILE *fd)
+{
+ outstab(st, fd, 0);
+}
+
+/* Outputs a node in indented tree form. This is
+ * not a full serialization, but mainly an aid for
+ * understanding and debugging. */
+static void outnode(Node *n, FILE *fd, int depth)
+{
+ size_t i;
+ char *ty;
+ char *tr;
+ int tid;
+ char buf[1024];
+
+ indent(fd, depth);
+ if (!n) {
+ fprintf(fd, "Nil\n");
+ return;
+ }
+ fprintf(fd, "%s", nodestr(n->type));
+ switch(n->type) {
+ case Nfile:
+ fprintf(fd, "(name = %s)\n", n->file.name);
+ indent(fd, depth + 1);
+ fprintf(fd, "Globls:\n");
+ outstab(n->file.globls, fd, depth + 2);
+ indent(fd, depth + 1);
+ fprintf(fd, "Exports:\n");
+ outstab(n->file.exports, fd, depth + 2);
+ for (i = 0; i < n->file.nuses; i++)
+ outnode(n->file.uses[i], fd, depth + 1);
+ for (i = 0; i < n->file.nstmts; i++)
+ outnode(n->file.stmts[i], fd, depth + 1);
+ break;
+ case Ndecl:
+ tr = "";
+ if (n->decl.trait)
+ tr = namestr(n->decl.trait->name);
+ fprintf(fd, "(did = %zd, trait=%s, isconst = %d, isgeneric = %d, isextern = %d, vis = %d)\n",
+ n->decl.did, tr, n->decl.isconst, n->decl.isgeneric, n->decl.isextern, n->decl.vis);
+ outsym(n, fd, depth + 1);
+ outnode(n->decl.init, fd, depth + 1);
+ break;
+ case Nblock:
+ fprintf(fd, "\n");
+ outstab(n->block.scope, fd, depth + 1);
+ for (i = 0; i < n->block.nstmts; i++)
+ outnode(n->block.stmts[i], fd, depth+1);
+ break;
+ case Nifstmt:
+ fprintf(fd, "\n");
+ outnode(n->ifstmt.cond, fd, depth+1);
+ outnode(n->ifstmt.iftrue, fd, depth+1);
+ outnode(n->ifstmt.iffalse, fd, depth+1);
+ break;
+ case Nloopstmt:
+ fprintf(fd, "\n");
+ outnode(n->loopstmt.init, fd, depth+1);
+ outnode(n->loopstmt.cond, fd, depth+1);
+ outnode(n->loopstmt.step, fd, depth+1);
+ outnode(n->loopstmt.body, fd, depth+1);
+ break;
+ case Niterstmt:
+ fprintf(fd, "\n");
+ outnode(n->iterstmt.elt, fd, depth+1);
+ outnode(n->iterstmt.seq, fd, depth+1);
+ outnode(n->iterstmt.body, fd, depth+1);
+ break;
+ case Nmatchstmt:
+ fprintf(fd, "\n");
+ outnode(n->matchstmt.val, fd, depth+1);
+ for (i = 0; i < n->matchstmt.nmatches; i++)
+ outnode(n->matchstmt.matches[i], fd, depth+1);
+ break;
+ case Nmatch:
+ fprintf(fd, "\n");
+ outnode(n->match.pat, fd, depth+1);
+ outnode(n->match.block, fd, depth+1);
+ break;
+ case Nuse:
+ fprintf(fd, " (name = %s, islocal = %d)\n", n->use.name, n->use.islocal);
+ break;
+ case Nexpr:
+ if (exprop(n) == Ovar)
+ assert(decls[n->expr.did]->decl.did == n->expr.did);
+ ty = tystr(n->expr.type);
+ if (n->expr.type)
+ tid = n->expr.type->tid;
+ else
+ tid = -1;
+ fprintf(fd, " (type = %s [tid %d], op = %s, isconst = %d, did=%zd)\n",
+ ty, tid, opstr(n->expr.op), n->expr.isconst, n->expr.did);
+ free(ty);
+ outnode(n->expr.idx, fd, depth + 1);
+ for (i = 0; i < n->expr.nargs; i++)
+ outnode(n->expr.args[i], fd, depth+1);
+ break;
+ case Nlit:
+ switch (n->lit.littype) {
+ case Lchr: fprintf(fd, " Lchr %c\n", n->lit.chrval); break;
+ case Lbool: fprintf(fd, " Lbool %s\n", n->lit.boolval ? "true" : "false"); break;
+ case Lint: fprintf(fd, " Lint %llu\n", n->lit.intval); break;
+ case Lflt: fprintf(fd, " Lflt %lf\n", n->lit.fltval); break;
+ case Lstr: fprintf(fd, " Lstr %s\n", n->lit.strval); break;
+ case Llbl: fprintf(fd, " Llbl %s\n", n->lit.lblval); break;
+ case Lfunc:
+ fprintf(fd, " Lfunc\n");
+ outnode(n->lit.fnval, fd, depth+1);
+ break;
+ }
+ break;
+ case Nfunc:
+ fprintf(fd, " (args =\n");
+ for (i = 0; i < n->func.nargs; i++)
+ outnode(n->func.args[i], fd, depth+1);
+ indent(fd, depth);
+ fprintf(fd, ")\n");
+ outstab(n->func.scope, fd, depth + 1);
+ outnode(n->func.body, fd, depth+1);
+ break;
+ case Nname:
+ fprintf(fd, "(");
+ if (n->name.ns)
+ fprintf(fd, "%s.", n->name.ns);
+ fprintf(fd, "%s", n->name.name);
+ fprintf(fd, ")\n");
+ break;
+ case Nimpl:
+ fprintf(fd, "(name = %s, type = %s)\n", namestr(n->impl.traitname), tyfmt(buf, sizeof buf, n->impl.type));
+ indent(fd, depth);
+ outnode(n->impl.traitname, fd, depth + 1);
+ for (i = 0; i < n->impl.ndecls; i++)
+ outnode(n->impl.decls[i], fd, depth+1);
+ break;
+ case Nnone:
+ fprintf(stderr, "Nnone not a real node type!");
+ fprintf(fd, "Nnone\n");
+ break;
+ }
+}
+
+void dump(Node *n, FILE *fd)
+{
+ outnode(n, fd, 0);
+}
--- /dev/null
+++ b/parse/gram.y
@@ -1,0 +1,970 @@
+%{
+#define YYERROR_VERBOSE
+#define YYDEBUG 1
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+
+Stab *curscope;
+
+void yyerror(const char *s);
+int yylex(void);
+
+static Op binop(int toktype);
+static Node *mkpseudodecl(Type *t);
+static void installucons(Stab *st, Type *t);
+static void addtrait(Type *t, char *str);
+
+%}
+
+%token<tok> Terror
+%token<tok> Tplus /* + */
+%token<tok> Tminus /* - */
+%token<tok> Tmul /* * */
+%token<tok> Tdiv /* / */
+%token<tok> Tinc /* ++ */
+%token<tok> Tdec /* -- */
+%token<tok> Tmod /* % */
+%token<tok> Tasn /* = */
+%token<tok> Taddeq /* += */
+%token<tok> Tsubeq /* -= */
+%token<tok> Tmuleq /* *= */
+%token<tok> Tdiveq /* /= */
+%token<tok> Tmodeq /* %= */
+%token<tok> Tboreq /* |= */
+%token<tok> Tbxoreq /* ^= */
+%token<tok> Tbandeq /* &= */
+%token<tok> Tbsleq /* <<= */
+%token<tok> Tbsreq /* >>= */
+
+%token<tok> Tbor /* | */
+%token<tok> Tbxor /* ^ */
+%token<tok> Tband /* & */
+%token<tok> Tbsl /* << */
+%token<tok> Tbsr /* >> */
+%token<tok> Tbnot /* ~ */
+
+%token<tok> Teq /* == */
+%token<tok> Tgt /* > */
+%token<tok> Tlt /* < */
+%token<tok> Tge /* >= */
+%token<tok> Tle /* <= */
+%token<tok> Tne /* != */
+
+%token<tok> Tlor /* || */
+%token<tok> Tland /* && */
+%token<tok> Tlnot /* ! */
+
+%token<tok> Tobrace /* { */
+%token<tok> Tcbrace /* } */
+%token<tok> Toparen /* ( */
+%token<tok> Tcparen /* ) */
+%token<tok> Tosqbrac /* [ */
+%token<tok> Tcsqbrac /* ] */
+%token<tok> Tat /* @ */
+%token<tok> Ttick /* ` */
+%token<tok> Tderef /* # */
+
+%token<tok> Ttype /* type */
+%token<tok> Tfor /* for */
+%token<tok> Tin /* in */
+%token<tok> Twhile /* while */
+%token<tok> Tif /* if */
+%token<tok> Telse /* else */
+%token<tok> Telif /* else */
+%token<tok> Tmatch /* match */
+%token<tok> Tgoto /* goto */
+%token<tok> Tbreak /* break */
+%token<tok> Tcontinue /* continue */
+
+%token<tok> Tintlit
+%token<tok> Tstrlit
+%token<tok> Tfloatlit
+%token<tok> Tchrlit
+%token<tok> Tboollit
+
+%token<tok> Ttrait /* trait */
+%token<tok> Timpl /* trait */
+%token<tok> Tstruct /* struct */
+%token<tok> Tunion /* union */
+%token<tok> Ttyparam /* @typename */
+
+%token<tok> Tconst /* const */
+%token<tok> Tvar /* var */
+%token<tok> Tgeneric /* var */
+%token<tok> Textern /* extern */
+%token<tok> Tcast /* castto */
+
+%token<tok> Texport /* export */
+%token<tok> Tprotect /* protect */
+
+%token<tok> Tellipsis/* ... */
+%token<tok> Tendln /* ; or \n */
+%token<tok> Tendblk /* ;; */
+%token<tok> Tcolon /* : */
+%token<tok> Twith /* :: */
+%token<tok> Tdot /* . */
+%token<tok> Tcomma /* , */
+%token<tok> Tret /* -> */
+%token<tok> Tuse /* use */
+%token<tok> Tpkg /* pkg */
+%token<tok> Tsizeof /* sizeof */
+
+%token<tok> Tident
+%token<tok> Teof
+
+%start file
+
+%type <ty> type structdef uniondef tupledef compoundtype functype funcsig
+%type <ty> generictype
+%type <tylist> typelist typarams
+%type <nodelist> typaramlist
+
+%type <tok> asnop cmpop addop mulop shiftop optident
+
+%type <tydef> tydef typeid
+%type <trait> traitdef
+
+%type <node> exprln retexpr goto continue break expr atomicexpr
+%type <node> littok literal asnexpr lorexpr landexpr borexpr
+%type <node> bandexpr cmpexpr unionexpr addexpr mulexpr shiftexpr prefixexpr postfixexpr
+%type <node> funclit seqlit tuplit name block stmt label use
+%type <node> declbody declcore structent arrayelt structelt tuphead
+%type <node> ifstmt forstmt whilestmt matchstmt elifs optexprln optexpr
+%type <node> match
+%type <node> castexpr
+%type <ucon> unionelt
+%type <node> blkbody
+%type <node> implstmt
+
+%type <nodelist> arglist argdefs params matches
+%type <nodelist> structbody structelts arrayelts
+%type <nodelist> tupbody tuprest
+%type <nodelist> decl decllist
+%type <nodelist> traitbody implbody
+
+%type <uconlist> unionbody
+
+%union {
+ struct {
+ int line;
+ Node **nl;
+ size_t nn;
+ } nodelist;
+ struct {
+ int line;
+ Ucon **ucl;
+ size_t nucl;
+ } uconlist;
+ struct {
+ int line;
+ Type **types;
+ size_t ntypes;
+ } tylist;
+ struct { /* FIXME: unused */
+ int line;
+ char *name;
+ Type *type;
+ Type **params;
+ size_t nparams;
+ } tydef;
+ Trait *trait;
+ Node *node;
+ Tok *tok;
+ Type *ty;
+ Ucon *ucon;
+}
+
+%%
+
+file : toplev
+ | file Tendln toplev
+ ;
+
+toplev : package
+ | use {lappend(&file->file.uses, &file->file.nuses, $1);}
+ | implstmt {
+ lappend(&file->file.stmts, &file->file.nstmts, $1);
+ putimpl(file->file.globls, $1);
+ }
+ | traitdef {
+ size_t i;
+ puttrait(file->file.globls, $1->name, $1);
+ for (i = 0; i < $1->nfuncs; i++)
+ putdcl(file->file.exports, $1->funcs[i]);
+ }
+ | tydef {
+ puttype(file->file.globls, mkname($1.line, $1.name), $1.type);
+ installucons(file->file.globls, $1.type);
+ }
+ | decl {
+ size_t i;
+ for (i = 0; i < $1.nn; i++) {
+ lappend(&file->file.stmts, &file->file.nstmts, $1.nl[i]);
+ $1.nl[i]->decl.isglobl = 1;
+ putdcl(file->file.globls, $1.nl[i]);
+ }
+ }
+ | /* empty */
+ ;
+
+decl : Tvar decllist {$$ = $2;}
+ | Tconst decllist {
+ size_t i;
+ for (i = 0; i < $2.nn; i++)
+ $2.nl[i]->decl.isconst = 1;
+ $$ = $2;
+ }
+ | Tgeneric decllist {
+ size_t i;
+ for (i = 0; i < $2.nn; i++) {
+ $2.nl[i]->decl.isconst = 1;
+ $2.nl[i]->decl.isgeneric = 1;
+ }
+ $$ = $2;}
+ | Textern Tvar decllist {
+ size_t i;
+ for (i = 0; i < $3.nn; i++)
+ $3.nl[i]->decl.isextern = 1;
+ $$ = $3;
+ }
+ | Textern Tconst decllist {
+ size_t i;
+ for (i = 0; i < $3.nn; i++) {
+ $3.nl[i]->decl.isconst = 1;
+ $3.nl[i]->decl.isextern = 1;
+ }
+ $$ = $3;
+ }
+ ;
+
+decllist: declbody {
+ $$.nl = NULL; $$.nn = 0;
+ lappend(&$$.nl, &$$.nn, $1);
+ }
+ | declbody Tcomma decllist {
+ linsert(&$3.nl, &$3.nn, 0, $1);
+ $$=$3;
+ }
+ ;
+
+use : Tuse Tident {$$ = mkuse($1->line, $2->str, 0);}
+ | Tuse Tstrlit {$$ = mkuse($1->line, $2->str, 1);}
+ ;
+
+optident: Tident {$$ = $1;}
+ | /* empty */ {$$ = NULL;}
+ ;
+
+package : Tpkg optident Tasn pkgbody Tendblk {
+ if (file->file.exports->name)
+ fatal($1->line, "Package already declared\n");
+ if ($2) {
+ updatens(file->file.exports, $2->str);
+ updatens(file->file.globls, $2->str);
+ }
+ }
+ ;
+
+pkgbody : pkgitem
+ | pkgbody Tendln pkgitem
+ ;
+
+pkgitem : decl {
+ size_t i;
+ for (i = 0; i < $1.nn; i++) {
+ putdcl(file->file.exports, $1.nl[i]);
+ if ($1.nl[i]->decl.init)
+ lappend(&file->file.stmts, &file->file.nstmts, $1.nl[i]);
+ }
+ }
+ | tydef {
+ puttype(file->file.exports, mkname($1.line, $1.name), $1.type);
+ installucons(file->file.exports, $1.type);
+ }
+ | traitdef {
+ size_t i;
+ $1->vis = Visexport;
+ puttrait(file->file.exports, $1->name, $1);
+ for (i = 0; i < $1->nfuncs; i++)
+ putdcl(file->file.exports, $1->funcs[i]);
+ }
+ | implstmt {
+ $1->impl.vis = Visexport;
+ putimpl(file->file.exports, $1);
+ }
+ | visdef {die("Unimplemented visdef");}
+ | /* empty */
+ ;
+
+visdef : Texport Tcolon
+ | Tprotect Tcolon
+ ;
+
+declbody: declcore Tasn expr {$$ = $1; $1->decl.init = $3;}
+ | declcore
+ ;
+
+declcore: name {$$ = mkdecl($1->line, $1, mktyvar($1->line));}
+ | name Tcolon type {$$ = mkdecl($1->line, $1, $3);}
+ ;
+
+name : Tident {$$ = mkname($1->line, $1->str);}
+ | Tident Tdot name {$$ = $3; setns($3, $1->str);}
+ ;
+
+implstmt: Timpl name type {
+ $$ = mkimplstmt($1->line, $2, $3, NULL, 0);
+ $$->impl.isproto = 1;
+ }
+ | Timpl name type Tasn Tendln implbody Tendblk {
+ $$ = mkimplstmt($1->line, $2, $3, $6.nl, $6.nn);
+ }
+ ;
+
+implbody
+ : optendlns {$$.nl = NULL; $$.nn = 0;}
+ | implbody Tident Tasn exprln optendlns {
+ Node *d;
+ $$ = $1;
+ d = mkdecl($2->line, mkname($2->line, $2->str), mktyvar($2->line));
+ d->decl.init = $4;
+ d->decl.isconst = 1;
+ lappend(&$$.nl, &$$.nn, d);
+ }
+ ;
+
+traitdef: Ttrait Tident generictype /* trait prototype */ {
+ $$ = mktrait($1->line, mkname($2->line, $2->str), $3, NULL, 0, NULL, 0, 1);
+ }
+ | Ttrait Tident generictype Tasn traitbody Tendblk /* trait definition */ {
+ size_t i;
+ $$ = mktrait($1->line, mkname($2->line, $2->str), $3, NULL, 0, $5.nl, $5.nn, 0);
+ for (i = 0; i < $5.nn; i++) {
+ $5.nl[i]->decl.trait = $$;
+ $5.nl[i]->decl.isgeneric = 1;
+ }
+ }
+ ;
+
+traitbody
+ : optendlns {$$.nl = NULL; $$.nn = 0;}
+ | traitbody Tident Tcolon type optendlns {
+ Node *d;
+ $$ = $1;
+ d = mkdecl($2->line, mkname($2->line, $2->str), $4);
+ d->decl.isgeneric = 1;
+ lappend(&$$.nl, &$$.nn, d);
+ }
+ ;
+
+
+tydef : Ttype typeid {$$ = $2;}
+ | Ttype typeid Tasn type {
+ $$ = $2;
+ $$.type = mktyname($2.line, mkname($2.line, $2.name), $2.params, $2.nparams, $4);
+ }
+ ;
+
+typeid : Tident {
+ $$.line = $1->line;
+ $$.name = $1->str;
+ $$.params = NULL;
+ $$.type = NULL;
+ }
+ | Tident Toparen typarams Tcparen {
+ $$.line = $1->line;
+ $$.name = $1->str;
+ $$.params = $3.types;
+ $$.nparams = $3.ntypes;
+ $$.type = NULL;
+ }
+ ;
+
+typarams: generictype {
+ $$.types = NULL; $$.ntypes = 0;
+ lappend(&$$.types, &$$.ntypes, $1);
+ }
+ | typarams Tcomma generictype {lappend(&$$.types, &$$.ntypes, $3);}
+ ;
+
+type : structdef
+ | tupledef
+ | uniondef
+ | compoundtype
+ | generictype
+ | Tellipsis {$$ = mktype($1->line, Tyvalist);}
+ ;
+
+generictype
+ : Ttyparam {$$ = mktyparam($1->line, $1->str);}
+ | Ttyparam Twith name {
+ $$ = mktyparam($1->line, $1->str);
+ addtrait($$, $3->name.name);
+ }
+ | Ttyparam Twith Toparen typaramlist Tcparen {
+ size_t i;
+ $$ = mktyparam($1->line, $1->str);
+ for (i = 0; i < $4.nn; i++)
+ addtrait($$, $4.nl[i]->name.name);
+ }
+ ;
+
+typaramlist
+ : name {
+ $$.nl = NULL; $$.nn = 0;
+ lappend(&$$.nl, &$$.nn, $1);
+ }
+ | typaramlist Tcomma name {lappend(&$$.nl, &$$.nn, $3);}
+ ;
+
+compoundtype
+ : functype {$$ = $1;}
+ | type Tosqbrac Tcolon Tcsqbrac {$$ = mktyslice($2->line, $1);}
+ | type Tosqbrac expr Tcsqbrac {$$ = mktyarray($2->line, $1, $3);}
+ | type Tderef {$$ = mktyptr($2->line, $1);}
+ | Tat Tident {$$ = mktyparam($1->line, $2->str);}
+ | name {$$ = mktyunres($1->line, $1, NULL, 0);}
+ | name Toparen typelist Tcparen {$$ = mktyunres($1->line, $1, $3.types, $3.ntypes);}
+ ;
+
+functype: Toparen funcsig Tcparen {$$ = $2;}
+ ;
+
+funcsig : argdefs
+ {$$ = mktyfunc($1.line, $1.nl, $1.nn, mktyvar($1.line));}
+ | argdefs Tret type
+ {$$ = mktyfunc($1.line, $1.nl, $1.nn, $3);}
+ ;
+
+argdefs : declcore {
+ $$.line = $1->line;
+ $$.nl = NULL;
+ $$.nn = 0; lappend(&$$.nl, &$$.nn, $1);
+ }
+ | argdefs Tcomma declcore {lappend(&$$.nl, &$$.nn, $3);}
+ | /* empty */ {
+ $$.line = line;
+ $$.nl = NULL;
+ $$.nn = 0;
+ }
+ ;
+
+tupledef: Tosqbrac typelist Tcsqbrac
+ {$$ = mktytuple($1->line, $2.types, $2.ntypes);}
+ ;
+
+typelist: type {
+ $$.types = NULL; $$.ntypes = 0;
+ lappend(&$$.types, &$$.ntypes, $1);
+ }
+ | typelist Tcomma type
+ {lappend(&$$.types, &$$.ntypes, $3);}
+ ;
+
+structdef
+ : Tstruct structbody Tendblk
+ {$$ = mktystruct($1->line, $2.nl, $2.nn);}
+ ;
+
+structbody
+ : structent {
+ if ($1) {
+ $$.nl = NULL;
+ $$.nn = 0;
+ lappend(&$$.nl, &$$.nn, $1);
+ }
+ }
+ | structbody structent {
+ if ($2)
+ lappend(&$$.nl, &$$.nn, $2);
+ }
+ ;
+
+structent
+ : declcore Tendln {$$ = $1;}
+ | visdef Tendln {$$ = NULL;}
+ | Tendln {$$ = NULL;}
+ ;
+
+uniondef
+ : Tunion unionbody Tendblk
+ {$$ = mktyunion($1->line, $2.ucl, $2.nucl);}
+ ;
+
+unionbody
+ : unionelt {
+ $$.ucl = NULL;
+ $$.nucl = 0;
+ if ($1)
+ lappend(&$$.ucl, &$$.nucl, $1);
+ }
+ | unionbody unionelt {
+ if ($2)
+ lappend(&$$.ucl, &$$.nucl, $2);
+ }
+ ;
+
+unionelt /* nb: the ucon union type gets filled in when we have context */
+ : Ttick name type Tendln {$$ = mkucon($2->line, $2, NULL, $3);}
+ | Ttick name Tendln {$$ = mkucon($2->line, $2, NULL, NULL);}
+ | visdef Tendln {$$ = NULL;}
+ | Tendln {$$ = NULL;}
+ ;
+
+goto : Tgoto Tident {$$ = mkexpr($1->line, Ojmp, mklbl($2->line, $2->str), NULL);}
+ ;
+
+retexpr : Tret expr {$$ = mkexpr($1->line, Oret, $2, NULL);}
+ | Tret {$$ = mkexpr($1->line, Oret, NULL);}
+ | expr
+ ;
+
+optexpr : expr {$$ = $1;}
+ | /* empty */ {$$ = NULL;}
+ ;
+
+optexprln: exprln {$$ = $1;}
+ | Tendln {$$ = NULL;}
+ ;
+
+exprln : expr Tendln
+ ;
+
+expr : asnexpr
+ ;
+
+asnexpr : lorexpr asnop asnexpr
+ {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+ | lorexpr
+ ;
+
+asnop : Tasn
+ | Taddeq /* += */
+ | Tsubeq /* -= */
+ | Tmuleq /* *= */
+ | Tdiveq /* /= */
+ | Tmodeq /* %= */
+ | Tboreq /* |= */
+ | Tbxoreq /* ^= */
+ | Tbandeq /* &= */
+ | Tbsleq /* <<= */
+ | Tbsreq /* >>= */
+ ;
+
+lorexpr : lorexpr Tlor landexpr
+ {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+ | landexpr
+ ;
+
+landexpr: landexpr Tland cmpexpr
+ {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+ | cmpexpr
+ ;
+
+cmpexpr : cmpexpr cmpop castexpr
+ {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+ | castexpr
+ ;
+
+
+cmpop : Teq | Tgt | Tlt | Tge | Tle | Tne ;
+
+castexpr: castexpr Tcast Toparen type Tcparen {
+ $$ = mkexpr($1->line, Ocast, $1, NULL);
+ $$->expr.type = $4;
+ }
+ | unionexpr
+ ;
+
+unionexpr
+ : Ttick name unionexpr {$$ = mkexpr($1->line, Oucon, $2, $3, NULL);}
+ | Ttick name {$$ = mkexpr($1->line, Oucon, $2, NULL);}
+ | borexpr
+ ;
+
+
+borexpr : borexpr Tbor bandexpr
+ {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+ | borexpr Tbxor bandexpr
+ {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+ | bandexpr
+ ;
+
+bandexpr: bandexpr Tband addexpr
+ {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+ | addexpr
+ ;
+
+addexpr : addexpr addop mulexpr
+ {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+ | mulexpr
+ ;
+
+addop : Tplus | Tminus ;
+
+mulexpr : mulexpr mulop shiftexpr
+ {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+ | shiftexpr
+ ;
+
+mulop : Tmul | Tdiv | Tmod
+ ;
+
+shiftexpr
+ : shiftexpr shiftop prefixexpr
+ {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+ | prefixexpr
+ ;
+
+shiftop : Tbsl | Tbsr;
+
+prefixexpr
+ : Tinc prefixexpr {$$ = mkexpr($1->line, Opreinc, $2, NULL);}
+ | Tdec prefixexpr {$$ = mkexpr($1->line, Opredec, $2, NULL);}
+ | Tband prefixexpr {$$ = mkexpr($1->line, Oaddr, $2, NULL);}
+ | Tlnot prefixexpr {$$ = mkexpr($1->line, Olnot, $2, NULL);}
+ | Tbnot prefixexpr {$$ = mkexpr($1->line, Obnot, $2, NULL);}
+ | Tminus prefixexpr {$$ = mkexpr($1->line, Oneg, $2, NULL);}
+ | Tplus prefixexpr {$$ = $2;} /* positive is a nop */
+ | postfixexpr
+ ;
+
+postfixexpr
+ : postfixexpr Tdot Tident
+ {$$ = mkexpr($1->line, Omemb, $1, mkname($3->line, $3->str), NULL);}
+ | postfixexpr Tinc
+ {$$ = mkexpr($1->line, Opostinc, $1, NULL);}
+ | postfixexpr Tdec
+ {$$ = mkexpr($1->line, Opostdec, $1, NULL);}
+ | postfixexpr Tosqbrac expr Tcsqbrac
+ {$$ = mkexpr($1->line, Oidx, $1, $3, NULL);}
+ | postfixexpr Tosqbrac optexpr Tcolon optexpr Tcsqbrac
+ {$$ = mksliceexpr($1->line, $1, $3, $5);}
+ | postfixexpr Tderef
+ {$$ = mkexpr($1->line, Oderef, $1, NULL);}
+ | postfixexpr Toparen arglist Tcparen
+ {$$ = mkcall($1->line, $1, $3.nl, $3.nn);}
+ | atomicexpr
+ ;
+
+arglist : asnexpr
+ {$$.nl = NULL; $$.nn = 0; lappend(&$$.nl, &$$.nn, $1);}
+ | arglist Tcomma asnexpr
+ {lappend(&$$.nl, &$$.nn, $3);}
+ | /* empty */
+ {$$.nl = NULL; $$.nn = 0;}
+ ;
+
+atomicexpr
+ : Tident
+ {$$ = mkexpr($1->line, Ovar, mkname($1->line, $1->str), NULL);}
+ | literal
+ | Toparen expr Tcparen
+ {$$ = $2;}
+ | Tsizeof Toparen type Tcparen
+ {$$ = mkexpr($1->line, Osize, mkpseudodecl($3), NULL);}
+ ;
+
+tupbody : tuphead tuprest
+ {$$ = $2;
+ linsert(&$$.nl, &$$.nn, 0, $1);}
+ ;
+
+tuphead : expr Tcomma {$$ = $1;}
+ ;
+
+tuprest : /*empty */
+ {$$.nl = NULL; $$.nn = 0;}
+ | expr {
+ $$.nl = NULL; $$.nn = 0;
+ lappend(&$$.nl, &$$.nn, $1);
+ }
+ | tuprest Tcomma expr {lappend(&$$.nl, &$$.nn, $3);}
+ ;
+
+literal : funclit {$$ = mkexpr($1->line, Olit, $1, NULL);}
+ | littok {$$ = mkexpr($1->line, Olit, $1, NULL);}
+ | seqlit {$$ = $1;}
+ | tuplit {$$ = $1;}
+ ;
+
+tuplit : Toparen tupbody Tcparen
+ {$$ = mkexprl($1->line, Otup, $2.nl, $2.nn);}
+
+littok : Tstrlit {$$ = mkstr($1->line, $1->str);}
+ | Tchrlit {$$ = mkchar($1->line, $1->chrval);}
+ | Tfloatlit {$$ = mkfloat($1->line, $1->fltval);}
+ | Tboollit {$$ = mkbool($1->line, !strcmp($1->str, "true"));}
+ | Tintlit {
+ $$ = mkint($1->line, $1->intval);
+ if ($1->inttype)
+ $$->lit.type = mktype($1->line, $1->inttype);
+ }
+ ;
+
+funclit : Tobrace params Tendln blkbody Tcbrace
+ {$$ = mkfunc($1->line, $2.nl, $2.nn, mktyvar($3->line), $4);}
+ | Tobrace params Tret type Tendln blkbody Tcbrace
+ {$$ = mkfunc($1->line, $2.nl, $2.nn, $4, $6);}
+ ;
+
+params : declcore {
+ $$.nl = NULL;
+ $$.nn = 0;
+ lappend(&$$.nl, &$$.nn, $1);
+ }
+ | params Tcomma declcore {lappend(&$$.nl, &$$.nn, $3);}
+ | /* empty */ {$$.nl = NULL; $$.nn = 0;}
+ ;
+
+seqlit : Tosqbrac arrayelts Tcsqbrac
+ {$$ = mkexprl($1->line, Oarr, $2.nl, $2.nn);}
+ | Tosqbrac structelts Tcsqbrac
+ {$$ = mkexprl($1->line, Ostruct, $2.nl, $2.nn);}
+ | Tosqbrac Tcsqbrac /* [] is the empty array. */
+ {$$ = mkexprl($1->line, Oarr, NULL, 0);}
+ ;
+
+arrayelts
+ : optendlns arrayelt {
+ $$.nl = NULL;
+ $$.nn = 0;
+ lappend(&$$.nl, &$$.nn, mkidxinit($2->line, mkint($2->line, 0), $2));
+ }
+ | arrayelts Tcomma optendlns arrayelt
+ {lappend(&$$.nl, &$$.nn, mkidxinit($4->line, mkint($4->line, $$.nn), $4));}
+ | arrayelts Tcomma optendlns
+ ;
+
+arrayelt: expr optendlns {$$ = $1;}
+ ;
+
+structelts
+ : structelt {
+ $$.nl = NULL;
+ $$.nn = 0;
+ lappend(&$$.nl, &$$.nn, $1);
+ }
+ | structelts Tcomma structelt
+ {lappend(&$$.nl, &$$.nn, $3);}
+ ;
+
+structelt: optendlns Tdot Tident Tasn expr optendlns
+ {$$ = mkidxinit($2->line, mkname($3->line, $3->str), $5);}
+ ;
+
+optendlns : /* none */
+ | optendlns Tendln
+ ;
+
+stmt : goto
+ | break
+ | continue
+ | retexpr
+ | label
+ | ifstmt
+ | forstmt
+ | whilestmt
+ | matchstmt
+ | /* empty */ {$$ = NULL;}
+ ;
+
+break : Tbreak
+ {$$ = mkexpr($1->line, Obreak, NULL);}
+ ;
+
+continue : Tcontinue
+ {$$ = mkexpr($1->line, Ocontinue, NULL);}
+ ;
+
+forstmt : Tfor optexprln optexprln optexprln block
+ {$$ = mkloopstmt($1->line, $2, $3, $4, $5);}
+ | Tfor expr Tin exprln block
+ {$$ = mkiterstmt($1->line, $2, $4, $5);}
+ /* FIXME: allow decls in for loops
+ | Tfor decl Tendln optexprln optexprln block
+ {$$ = mkloopstmt($1->line, $2, $4, $5, $6);}
+ */
+ ;
+
+whilestmt
+ : Twhile exprln block
+ {$$ = mkloopstmt($1->line, NULL, $2, NULL, $3);}
+ ;
+
+ifstmt : Tif exprln blkbody elifs
+ {$$ = mkifstmt($1->line, $2, $3, $4);}
+ ;
+
+elifs : Telif exprln blkbody elifs
+ {$$ = mkifstmt($1->line, $2, $3, $4);}
+ | Telse block
+ {$$ = $2;}
+ | Tendblk
+ {$$ = NULL;}
+ ;
+
+matchstmt: Tmatch exprln optendlns Tbor matches Tendblk
+ {$$ = mkmatchstmt($1->line, $2, $5.nl, $5.nn);}
+ ;
+
+matches : match {
+ $$.nl = NULL;
+ $$.nn = 0;
+ if ($1)
+ lappend(&$$.nl, &$$.nn, $1);
+ }
+ | matches Tbor match {
+ if ($2)
+ lappend(&$$.nl, &$$.nn, $3);
+ }
+ ;
+
+match : expr Tcolon blkbody Tendln {$$ = mkmatch($1->line, $1, $3);}
+ ;
+
+block : blkbody Tendblk
+ ;
+
+blkbody : decl {
+ size_t i;
+ $$ = mkblock(line, mkstab());
+ for (i = 0; i < $1.nn; i++) {
+ putdcl($$->block.scope, $1.nl[i]);
+ lappend(&$$->block.stmts, &$$->block.nstmts, $1.nl[i]);
+ }
+ }
+ | stmt {
+ $$ = mkblock(line, mkstab());
+ if ($1)
+ lappend(&$$->block.stmts, &$$->block.nstmts, $1);
+ }
+ | blkbody Tendln stmt {
+ if ($3)
+ lappend(&$1->block.stmts, &$1->block.nstmts, $3);
+ $$ = $1;
+ }
+ | blkbody Tendln decl {
+ size_t i;
+ for (i = 0; i < $3.nn; i++){
+ putdcl($$->block.scope, $3.nl[i]);
+ lappend(&$1->block.stmts, &$1->block.nstmts, $3.nl[i]);
+ }
+ }
+ ;
+
+label : Tcolon Tident
+ {$$ = mklbl($2->line, $2->str);}
+ ;
+
+%%
+
+static void addtrait(Type *t, char *str)
+{
+ size_t i;
+
+ for (i = 0; i < ntraittab; i++) {
+ if (!strcmp(namestr(traittab[i]->name), str)) {
+ settrait(t, traittab[i]);
+ return;
+ }
+ }
+ fatal(t->line, "Constraint %s does not exist", str);
+}
+
+static Node *mkpseudodecl(Type *t)
+{
+ static int nextpseudoid;
+ char buf[128];
+
+ snprintf(buf, 128, ".pdecl%d", nextpseudoid++);
+ return mkdecl(-1, mkname(-1, buf), t);
+}
+
+static void installucons(Stab *st, Type *t)
+{
+ Type *b;
+ size_t i;
+
+ if (!t)
+ return;
+ b = tybase(t);
+ switch (b->type) {
+ case Tystruct:
+ for (i = 0; i < b->nmemb; i++)
+ installucons(st, b->sdecls[i]->decl.type);
+ break;
+ case Tyunion:
+ for (i = 0; i < b->nmemb; i++) {
+ b->udecls[i]->utype = t;
+ b->udecls[i]->id = i;
+ putucon(st, b->udecls[i]);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void yyerror(const char *s)
+{
+ fprintf(stderr, "%s:%d: %s", filename, line, s);
+ if (curtok->str)
+ fprintf(stderr, " near \"%s\"", curtok->str);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+static Op binop(int tt)
+{
+ Op o;
+
+ o = Obad;
+ switch (tt) {
+ case Tplus: o = Oadd; break;
+ case Tminus: o = Osub; break;
+ case Tmul: o = Omul; break;
+ case Tdiv: o = Odiv; break;
+ case Tmod: o = Omod; break;
+ case Tasn: o = Oasn; break;
+ case Taddeq: o = Oaddeq; break;
+ case Tsubeq: o = Osubeq; break;
+ case Tmuleq: o = Omuleq; break;
+ case Tdiveq: o = Odiveq; break;
+ case Tmodeq: o = Omodeq; break;
+ case Tboreq: o = Oboreq; break;
+ case Tbxoreq: o = Obxoreq; break;
+ case Tbandeq: o = Obandeq; break;
+ case Tbsleq: o = Obsleq; break;
+ case Tbsreq: o = Obsreq; break;
+ case Tbor: o = Obor; break;
+ case Tbxor: o = Obxor; break;
+ case Tband: o = Oband; break;
+ case Tbsl: o = Obsl; break;
+ case Tbsr: o = Obsr; break;
+ case Teq: o = Oeq; break;
+ case Tgt: o = Ogt; break;
+ case Tlt: o = Olt; break;
+ case Tge: o = Oge; break;
+ case Tle: o = Ole; break;
+ case Tne: o = One; break;
+ case Tlor: o = Olor; break;
+ case Tland: o = Oland; break;
+ default:
+ die("Unimplemented binop\n");
+ break;
+ }
+ return o;
+}
+
--- /dev/null
+++ b/parse/htab.c
@@ -1,0 +1,256 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+
+#include "parse.h"
+
+#define Initsz 16
+
+/* Creates a new empty hash table, using 'hash' as the
+ * hash funciton, and 'cmp' to verify that there are no
+ * hash collisions. */
+Htab *mkht(ulong (*hash)(void *key), int (*cmp)(void *k1, void *k2))
+{
+ Htab *ht;
+
+ ht = xalloc(sizeof(Htab));
+ ht->nelt = 0;
+ ht->sz = Initsz;
+ ht->hash = hash;
+ ht->cmp = cmp;
+ ht->keys = zalloc(Initsz*sizeof(void*));
+ ht->vals = zalloc(Initsz*sizeof(void*));
+ ht->hashes = zalloc(Initsz*sizeof(void*));
+ ht->dead = zalloc(Initsz*sizeof(char));
+
+ return ht;
+}
+
+/* Frees a hash table. Passing this function
+ * NULL is a no-op. */
+void htfree(Htab *ht)
+{
+ if (!ht)
+ return;
+ free(ht->keys);
+ free(ht->vals);
+ free(ht->hashes);
+ free(ht->dead);
+ free(ht);
+}
+
+/* Offsets the hash so that '0' can be
+ * used as a 'no valid value */
+static ulong hash(Htab *ht, void *k)
+{
+ ulong h;
+ h = ht->hash(k);
+ if (h == 0)
+ return 1;
+ else
+ return h;
+}
+
+/* Resizes the hash table by copying all
+ * the old keys into the right slots in a
+ * new table. */
+static void grow(Htab *ht, int sz)
+{
+ void **oldk;
+ void **oldv;
+ ulong *oldh;
+ char *oldd;
+ int oldsz;
+ int i;
+
+ oldk = ht->keys;
+ oldv = ht->vals;
+ oldh = ht->hashes;
+ oldd = ht->dead;
+ oldsz = ht->sz;
+
+ ht->nelt = 0;
+ ht->sz = sz;
+ ht->keys = zalloc(sz*sizeof(void*));
+ ht->vals = zalloc(sz*sizeof(void*));
+ ht->hashes = zalloc(sz*sizeof(void*));
+ ht->dead = zalloc(sz*sizeof(void*));
+
+ for (i = 0; i < oldsz; i++)
+ if (oldh[i] && !oldd[i])
+ htput(ht, oldk[i], oldv[i]);
+ free(oldh);
+ free(oldk);
+ free(oldv);
+ free(oldd);
+}
+
+/* Inserts 'k' into the hash table, possibly
+ * killing any previous key that compares
+ * as equal. */
+int htput(Htab *ht, void *k, void *v)
+{
+ int i;
+ ulong h;
+ int di;
+
+ di = 0;
+ h = hash(ht, k);
+ i = h & (ht->sz - 1);
+ while (ht->hashes[i] && !ht->dead[i]) {
+ /* second insertion overwrites first. nb, we shouldn't touch the
+ * keys for dead values */
+ if (ht->hashes[i] == h) {
+ if (ht->dead[i])
+ break;
+ else if (ht->cmp(ht->keys[i], k))
+ goto conflicted;
+ }
+ di++;
+ i = (h + di) & (ht->sz - 1);
+ }
+ ht->nelt++;
+conflicted:
+ ht->hashes[i] = h;
+ ht->keys[i] = k;
+ ht->vals[i] = v;
+ ht->dead[i] = 0;
+ if (ht->sz < ht->nelt*2)
+ grow(ht, ht->sz*2);
+ return 1;
+}
+
+/* Finds the index that we would insert
+ * the key into */
+static ssize_t htidx(Htab *ht, void *k)
+{
+ ssize_t i;
+ ulong h;
+ int di;
+
+ di = 0;
+ h = hash(ht, k);
+ i = h & (ht->sz - 1);
+ while (ht->hashes[i] && !ht->dead[i] && ht->hashes[i] != h) {
+searchmore:
+ di++;
+ i = (h + di) & (ht->sz - 1);
+ }
+ if (!ht->hashes[i] || ht->dead[i])
+ return -1;
+ if (!ht->cmp(ht->keys[i], k))
+ goto searchmore; /* collision */
+ return i;
+}
+
+/* Looks up a key, returning NULL if
+ * the value is not present. Note,
+ * if NULL is a valid value, you need
+ * to check with hthas() to see if it's
+ * not there */
+void *htget(Htab *ht, void *k)
+{
+ ssize_t i;
+
+ i = htidx(ht, k);
+ if (i < 0)
+ return NULL;
+ else
+ return ht->vals[i];
+}
+
+void htdel(Htab *ht, void *k)
+{
+ ssize_t i;
+
+ i = htidx(ht, k);
+ if (i < 0)
+ return;
+ ht->dead[i] = 1;
+ ht->nelt--;
+}
+
+
+/* Tests for 'k's presence in 'ht' */
+int hthas(Htab *ht, void *k)
+{
+ return htidx(ht, k) >= 0;
+}
+
+/* Returns a list of all keys in the hash
+ * table, storing the size of the returned
+ * array in 'nkeys'. NB: the value returned
+ * is allocated on the heap, and it is the
+ * job of the caller to free it */
+void **htkeys(Htab *ht, size_t *nkeys)
+{
+ void **k;
+ size_t i, j;
+
+ j = 0;
+ k = xalloc(sizeof(void*)*ht->nelt);
+ for (i = 0; i < ht->sz; i++)
+ if (ht->hashes[i] && !ht->dead[i])
+ k[j++] = ht->keys[i];
+ *nkeys = ht->nelt;
+ return k;
+}
+
+ulong strhash(void *_s)
+{
+ char *s;
+ ulong h;
+ ulong g;
+
+ s = _s;
+ h = 0;
+ while (s && *s) {
+ h = ((h << 4) + *s++);
+
+ if ((g = (h & 0xF0000000)))
+ h ^= (g >> 24);
+
+ h &= ~g;
+ }
+ return h;
+}
+
+int streq(void *a, void *b)
+{
+ if (a == b)
+ return 1;
+ if (a == NULL || b == NULL)
+ return 0;
+ return !strcmp(a, b);
+}
+
+ulong ptrhash(void *key)
+{
+ return inthash((intptr_t)key);
+}
+
+ulong inthash(uint64_t key)
+{
+ intptr_t h;
+
+ h = (intptr_t) key;
+ h *= 357913941;
+ h ^= h << 24;
+ h += ~357913941;
+ h ^= h >> 31;
+ h ^= h << 31;
+ return h;
+}
+
+int inteq(uint64_t a, uint64_t b)
+{
+ return a == b;
+}
+
+int ptreq(void *a, void *b)
+{
+ return a == b;
+}
--- /dev/null
+++ b/parse/infer.c
@@ -1,0 +1,2109 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.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 <assert.h>
+
+#include "parse.h"
+
+typedef struct Inferstate Inferstate;
+struct Inferstate {
+ int ingeneric;
+ int sawret;
+ Type *ret;
+
+ /* bound by patterns turn into decls in the action block */
+ Node **binds;
+ size_t nbinds;
+ /* nodes that need post-inference checking/unification */
+ Node **postcheck;
+ size_t npostcheck;
+ Stab **postcheckscope;
+ size_t npostcheckscope;
+ /* the type params bound at the current point */
+ Htab **tybindings;
+ size_t ntybindings;
+ /* generic declarations to be specialized */
+ Node **genericdecls;
+ size_t ngenericdecls;
+ /* delayed unification -- we fall back to these types in a post pass if we
+ * haven't unifed to something more specific */
+ Htab *delayed;
+ /* the nodes that we've specialized them to, and the scopes they
+ * appear in */
+ Node **specializations;
+ size_t nspecializations;
+ Stab **specializationscope;
+ size_t nspecializationscope;
+};
+
+static void infernode(Inferstate *st, Node *n, Type *ret, int *sawret);
+static void inferexpr(Inferstate *st, Node *n, Type *ret, int *sawret);
+static void inferdecl(Inferstate *st, Node *n);
+static void typesub(Inferstate *st, Node *n);
+static void tybind(Inferstate *st, Type *t);
+static void bind(Inferstate *st, Node *n);
+static void tyunbind(Inferstate *st, Type *t);
+static void unbind(Inferstate *st, Node *n);
+static Type *unify(Inferstate *st, Node *ctx, Type *a, Type *b);
+static Type *tf(Inferstate *st, Type *t);
+
+/* Tries to give a good string describing the context
+ * for the sake of error messages. */
+static char *ctxstr(Inferstate *st, Node *n)
+{
+ char *s;
+ char *t;
+ char *u;
+ char *idx;
+ char buf[512];
+
+ idx = NULL;
+ switch (n->type) {
+ default:
+ s = strdup(nodestr(n->type));
+ break;
+ case Ndecl:
+ u = declname(n);
+ t = tystr(tf(st, decltype(n)));
+ snprintf(buf, sizeof buf, "%s:%s", u, t);
+ s = strdup(buf);
+ free(t);
+ break;
+ case Nname:
+ s = strdup(namestr(n));
+ break;
+ case Nexpr:
+ if (n->expr.idx)
+ idx = ctxstr(st, n->expr.idx);
+ if (exprop(n) == Ovar)
+ u = namestr(n->expr.args[0]);
+ else
+ u = opstr(exprop(n));
+ if (exprtype(n))
+ t = tystr(tf(st, exprtype(n)));
+ else
+ t = strdup("unknown");
+ if (idx)
+ snprintf(buf, sizeof buf, ".%s=%s:%s", idx, u, t);
+ else
+ snprintf(buf, sizeof buf, "%s:%s", u, t);
+ free(idx);
+ free(t);
+ s = strdup(buf);
+ break;
+ }
+ return s;
+}
+
+static void delayedcheck(Inferstate *st, Node *n, Stab *s)
+{
+ lappend(&st->postcheck, &st->npostcheck, n);
+ lappend(&st->postcheckscope, &st->npostcheckscope, s);
+}
+
+static void typeerror(Inferstate *st, Type *a, Type *b, Node *ctx, char *msg)
+{
+ char *t1, *t2, *c;
+
+ t1 = tystr(a);
+ t2 = tystr(b);
+ c = ctxstr(st, ctx);
+ if (msg)
+ fatal(ctx->line, "Type \"%s\" incompatible with \"%s\" near %s: %s", t1, t2, c, msg);
+ else
+ fatal(ctx->line, "Type \"%s\" incompatible with \"%s\" near %s", t1, t2, c);
+ free(t1);
+ free(t2);
+ free(c);
+}
+
+
+/* Set a scope's enclosing scope up correctly.
+ * We don't do this in the parser for some reason. */
+static void setsuper(Stab *st, Stab *super)
+{
+ Stab *s;
+
+ /* verify that we don't accidentally create loops */
+ for (s = super; s; s = s->super)
+ assert(s->super != st);
+ st->super = super;
+}
+
+/* If the current environment binds a type,
+ * we return true */
+static int isbound(Inferstate *st, Type *t)
+{
+ ssize_t i;
+ Type *p;
+
+ for (i = st->ntybindings - 1; i >= 0; i--) {
+ p = htget(st->tybindings[i], t->pname);
+ if (p == t)
+ return 1;
+ }
+ return 0;
+}
+
+/* Checks if a type that directly contains itself.
+ * Recursive types that contain themselves through
+ * pointers or slices are fine, but any other self-inclusion
+ * would lead to a value of infinite size */
+static int tyinfinite(Inferstate *st, Type *t, Type *sub)
+{
+ size_t i;
+
+ assert(t != NULL);
+ if (t == sub) /* FIXME: is this actually right? */
+ return 1;
+ /* if we're on the first iteration, the subtype is the type
+ * itself. The assignment must come after the equality check
+ * for obvious reasons. */
+ if (!sub)
+ sub = t;
+
+ switch (sub->type) {
+ case Tystruct:
+ for (i = 0; i < sub->nmemb; i++)
+ if (tyinfinite(st, t, decltype(sub->sdecls[i])))
+ return 1;
+ break;
+ case Tyunion:
+ for (i = 0; i < sub->nmemb; i++) {
+ if (sub->udecls[i]->etype && tyinfinite(st, t, sub->udecls[i]->etype))
+ return 1;
+ }
+ break;
+
+ case Typtr:
+ case Tyslice:
+ return 0;
+ default:
+ for (i = 0; i < sub->nsub; i++)
+ if (tyinfinite(st, t, sub->sub[i]))
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+
+static int needfreshen(Inferstate *st, Type *t)
+{
+ size_t i;
+
+ switch (t->type) {
+ case Typaram: return 1;
+ case Tyname: return isgeneric(t);
+ case Tystruct:
+ for (i = 0; i < t->nmemb; i++)
+ if (needfreshen(st, decltype(t->sdecls[i])))
+ return 1;
+ break;
+ case Tyunion:
+ for (i = 0; i < t->nmemb; i++)
+ if (t->udecls[i]->etype && needfreshen(st, t->udecls[i]->etype))
+ return 1;
+ break;
+ default:
+ for (i = 0; i < t->nsub; i++)
+ if (needfreshen(st, t->sub[i]))
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+/* Freshens the type of a declaration. */
+static Type *tyfreshen(Inferstate *st, Type *t)
+{
+ Htab *ht;
+
+ if (!needfreshen(st, t)) {
+ if (debugopt['u'])
+ printf("%s isn't generic: skipping freshen\n", tystr(t));
+ return t;
+ }
+
+ if (debugopt['u'])
+ printf("Freshen %s => ", tystr(t));
+ tybind(st, t);
+ ht = mkht(tyhash, tyeq);
+ t = tyspecialize(t, ht);
+ htfree(ht);
+ tyunbind(st, t);
+ if (debugopt['u'])
+ printf("%s\n", tystr(t));
+
+ return t;
+}
+
+
+/* Resolves a type and all it's subtypes recursively.*/
+static void tyresolve(Inferstate *st, Type *t)
+{
+ size_t i;
+ Type *base;
+
+ if (t->resolved)
+ return;
+ /* type resolution should never throw errors about non-generics
+ * showing up within a generic type, so we push and pop a generic
+ * around resolution */
+ st->ingeneric++;
+ t->resolved = 1;
+ /* Walk through aggregate type members */
+ if (t->type == Tystruct) {
+ for (i = 0; i < t->nmemb; i++)
+ infernode(st, t->sdecls[i], NULL, NULL);
+ } else if (t->type == Tyunion) {
+ for (i = 0; i < t->nmemb; i++) {
+ t->udecls[i]->utype = t;
+ t->udecls[i]->utype = tf(st, t->udecls[i]->utype);
+ if (t->udecls[i]->etype) {
+ tyresolve(st, t->udecls[i]->etype);
+ t->udecls[i]->etype = tf(st, t->udecls[i]->etype);
+ }
+ }
+ } else if (t->type == Tyarray) {
+ infernode(st, t->asize, NULL, NULL);
+ }
+
+ for (i = 0; i < t->nsub; i++)
+ t->sub[i] = tf(st, t->sub[i]);
+ base = tybase(t);
+ /* no-ops if base == t */
+ if (t->traits)
+ bsunion(t->traits, base->traits);
+ else
+ t->traits = bsdup(base->traits);
+ if (tyinfinite(st, t, NULL))
+ fatal(t->line, "Type %s includes itself", tystr(t));
+ st->ingeneric--;
+}
+
+/* Look up the best type to date in the unification table, returning it */
+static Type *tysearch(Inferstate *st, Type *t)
+{
+ Type *lu;
+ Stab *ns;
+
+ assert(t != NULL);
+ lu = NULL;
+ while (1) {
+ if (!tytab[t->tid] && t->type == Tyunres) {
+ ns = curstab();
+ if (t->name->name.ns) {
+ ns = getns_str(ns, t->name->name.ns);
+ }
+ if (!ns)
+ fatal(t->name->line, "Could not resolve namespace \"%s\"", t->name->name.ns);
+ if (!(lu = gettype(ns, t->name)))
+ fatal(t->name->line, "Could not resolve type %s", namestr(t->name));
+ tytab[t->tid] = lu;
+ }
+
+ if (!tytab[t->tid])
+ break;
+ /* compress paths: shift the link up one level */
+ if (tytab[tytab[t->tid]->tid])
+ tytab[t->tid] = tytab[tytab[t->tid]->tid];
+ t = tytab[t->tid];
+ }
+ return t;
+}
+
+/* fixd the most accurate type mapping we have (ie,
+ * the end of the unification chain */
+static Type *tf(Inferstate *st, Type *orig)
+{
+ Type *t;
+ size_t i;
+ int is;
+
+ t = tysearch(st, orig);
+ is = isgeneric(orig);
+ st->ingeneric += isgeneric(orig);
+ tyresolve(st, t);
+ /* If this is an instantiation of a generic type, we want the params to
+ * match the instantiation */
+ if (orig->type == Tyunres && isgeneric(t)) {
+ t = tyfreshen(st, t);
+ for (i = 0; i < t->narg; i++) {
+ unify(st, NULL, t->arg[i], orig->arg[i]);
+ }
+ }
+ assert(is == isgeneric(orig));
+ st->ingeneric -= isgeneric(orig);
+ return t;
+}
+
+/* set the type of any typable node */
+static void settype(Inferstate *st, Node *n, Type *t)
+{
+ t = tf(st, t);
+ switch (n->type) {
+ case Nexpr: n->expr.type = t; break;
+ case Ndecl: n->decl.type = t; break;
+ case Nlit: n->lit.type = t; break;
+ case Nfunc: n->func.type = t; break;
+ default:
+ die("untypable node %s", nodestr(n->type));
+ break;
+ }
+}
+
+/* Gets the type of a literal value */
+static Type *littype(Node *n)
+{
+ if (n->lit.type)
+ return n->lit.type;
+ switch (n->lit.littype) {
+ case Lchr: return mktype(n->line, Tychar); break;
+ case Lbool: return mktype(n->line, Tybool); break;
+ case Lint: return mktylike(n->line, Tyint); break;
+ case Lflt: return mktylike(n->line, Tyfloat64); break;
+ case Lstr: return mktyslice(n->line, mktype(n->line, Tybyte)); break;
+ case Llbl: return mktyptr(n->line, mktype(n->line, Tyvoid)); break;
+ case Lfunc: return n->lit.fnval->func.type; break;
+ };
+ die("Bad lit type %d", n->lit.littype);
+ return NULL;
+}
+
+static Type *delayeducon(Inferstate *st, Type *fallback)
+{
+ Type *t;
+
+ if (fallback->type != Tyunion)
+ return fallback;
+ t = mktylike(fallback->line, fallback->type);
+ htput(st->delayed, t, fallback);
+ return t;
+}
+
+/* Finds the type of any typable node */
+static Type *type(Inferstate *st, Node *n)
+{
+ Type *t;
+
+ switch (n->type) {
+ case Nlit: t = littype(n); break;
+ case Nexpr: t = n->expr.type; break;
+ case Ndecl: t = decltype(n); break;
+ case Nfunc: t = n->func.type; break;
+ default:
+ t = NULL;
+ die("untypeable node %s", nodestr(n->type));
+ break;
+ };
+ return tf(st, t);
+}
+
+static Ucon *uconresolve(Inferstate *st, Node *n)
+{
+ Ucon *uc;
+ Node **args;
+ Stab *ns;
+
+ args = n->expr.args;
+ ns = curstab();
+ if (args[0]->name.ns)
+ ns = getns_str(ns, args[0]->name.ns);
+ if (!ns)
+ fatal(n->line, "No namespace %s\n", args[0]->name.ns);
+ uc = getucon(ns, args[0]);
+ if (!uc)
+ fatal(n->line, "no union constructor `%s", ctxstr(st, args[0]));
+ if (!uc->etype && n->expr.nargs > 1)
+ fatal(n->line, "nullary union constructor `%s passed arg ", ctxstr(st, args[0]));
+ else if (uc->etype && n->expr.nargs != 2)
+ fatal(n->line, "union constructor `%s needs arg ", ctxstr(st, args[0]));
+ return uc;
+}
+
+/* Binds the type parameters present in the
+ * current type into the type environment */
+static void putbindings(Inferstate *st, Htab *bt, Type *t)
+{
+ size_t i;
+ char *s;
+
+ if (!t)
+ return;
+ if (t->type != Typaram)
+ return;
+
+ if (debugopt['u']) {
+ s = tystr(t);
+ printf("\tBind %s", s);
+ free(s);
+ }
+ if (hthas(bt, t->pname))
+ unify(st, NULL, htget(bt, t->pname), t);
+ else if (isbound(st, t))
+ return;
+
+ htput(bt, t->pname, t);
+ for (i = 0; i < t->narg; i++)
+ putbindings(st, bt, t->arg[i]);
+}
+
+static void tybind(Inferstate *st, Type *t)
+{
+ Htab *bt;
+ char *s;
+
+ if (t->type != Tyname && !isgeneric(t))
+ return;
+ if (debugopt['u']) {
+ s = tystr(t);
+ printf("Binding %s", s);
+ free(s);
+ }
+ bt = mkht(strhash, streq);
+ lappend(&st->tybindings, &st->ntybindings, bt);
+ putbindings(st, bt, t);
+}
+
+/* Binds the type parameters in the
+ * declaration into the type environment */
+static void bind(Inferstate *st, Node *n)
+{
+ Htab *bt;
+
+ assert(n->type == Ndecl);
+ if (!n->decl.isgeneric)
+ return;
+ if (!n->decl.init)
+ fatal(n->line, "generic %s has no initializer", n->decl);
+
+ st->ingeneric++;
+ bt = mkht(strhash, streq);
+ lappend(&st->tybindings, &st->ntybindings, bt);
+
+ putbindings(st, bt, n->decl.type);
+ putbindings(st, bt, n->decl.init->expr.type);
+}
+
+/* Rolls back the binding of type parameters in
+ * the type environment */
+static void unbind(Inferstate *st, Node *n)
+{
+ if (!n->decl.isgeneric)
+ return;
+ htfree(st->tybindings[st->ntybindings - 1]);
+ lpop(&st->tybindings, &st->ntybindings);
+ st->ingeneric--;
+}
+
+static void tyunbind(Inferstate *st, Type *t)
+{
+ if (t->type != Tyname && !isgeneric(t))
+ return;
+ htfree(st->tybindings[st->ntybindings - 1]);
+ lpop(&st->tybindings, &st->ntybindings);
+}
+
+/* Constrains a type to implement the required constraints. On
+ * type variables, the constraint is added to the required
+ * constraint list. Otherwise, the type is checked to see
+ * if it has the required constraint */
+static void constrain(Inferstate *st, Node *ctx, Type *a, Trait *c)
+{
+ if (a->type == Tyvar) {
+ if (!a->traits)
+ a->traits = mkbs();
+ settrait(a, c);
+ } else if (!a->traits || !bshas(a->traits, c->uid)) {
+ fatal(ctx->line, "%s needs %s near %s", tystr(a), namestr(c->name), ctxstr(st, ctx));
+ }
+}
+
+/* does b satisfy all the constraints of a? */
+static int checktraits(Type *a, Type *b)
+{
+ /* a has no traits to satisfy */
+ if (!a->traits)
+ return 1;
+ /* b satisfies no traits; only valid if a requires none */
+ if (!b->traits)
+ return bscount(a->traits) == 0;
+ /* if a->traits is a subset of b->traits, all of
+ * a's constraints are satisfied by b. */
+ return bsissubset(a->traits, b->traits);
+}
+
+/* Merges the constraints on types */
+static void mergetraits(Inferstate *st, Node *ctx, Type *a, Type *b)
+{
+ size_t i, n;
+ char *sep;
+ char traitbuf[1024], abuf[1024], bbuf[1024];
+
+ if (b->type == Tyvar) {
+ /* make sure that if a = b, both have same traits */
+ if (a->traits && b->traits)
+ bsunion(b->traits, a->traits);
+ else if (a->traits)
+ b->traits = bsdup(a->traits);
+ else if (b->traits)
+ a->traits = bsdup(b->traits);
+ } else {
+ if (!checktraits(a, b)) {
+ sep = "";
+ n = 0;
+ for (i = 0; bsiter(a->traits, &i); i++) {
+ if (!b->traits || !bshas(b->traits, i))
+ n += snprintf(traitbuf + n, sizeof(traitbuf) - n, "%s%s", sep, namestr(traittab[i]->name));
+ sep = ",";
+ }
+ tyfmt(abuf, sizeof abuf, a);
+ tyfmt(bbuf, sizeof bbuf, b);
+ fatal(ctx->line, "%s missing traits %s for %s near %s", bbuf, traitbuf, abuf, ctxstr(st, ctx));
+ }
+ }
+}
+
+/* Tells us if we have an index hack on the type */
+static int idxhacked(Type *a, Type *b)
+{
+ return (a->type == Tyvar && a->nsub > 0) || a->type == Tyarray || a->type == Tyslice;
+}
+
+/* prevents types that contain themselves in the unification;
+ * eg @a U (@a -> foo) */
+static int occurs(Type *a, Type *b)
+{
+ size_t i;
+
+ if (a == b)
+ return 1;
+ for (i = 0; i < b->nsub; i++)
+ if (occurs(a, b->sub[i]))
+ return 1;
+ return 0;
+}
+
+/* Computes the 'rank' of the type; ie, in which
+ * direction should we unify. A lower ranked type
+ * should be mapped to the higher ranked (ie, more
+ * specific) type. */
+static int tyrank(Type *t)
+{
+ /* plain tyvar */
+ if (t->type == Tyvar && t->nsub == 0)
+ return 0;
+ /* parameterized tyvar */
+ if (t->type == Tyvar && t->nsub > 0)
+ return 1;
+ /* concrete type */
+ return 2;
+}
+
+static int hasparam(Type *t)
+{
+ return t->type == Tyname && t->narg > 0;
+}
+
+static void membunify(Inferstate *st, Node *ctx, Type *u, Type *v) {
+ size_t i;
+
+ if (hthas(st->delayed, u))
+ u = htget(st->delayed, u);
+ u = tybase(u);
+ if (hthas(st->delayed, v))
+ v = htget(st->delayed, v);
+ v = tybase(v);
+ if (u->type == Tyunion && v->type == Tyunion && u != v) {
+ assert(u->nmemb = v->nmemb);
+ for (i = 0; i < v->nmemb; i++) {
+ if (u->udecls[i]->etype)
+ unify(st, ctx, u->udecls[i]->etype, v->udecls[i]->etype);
+ }
+ } else if (u->type == Tystruct && v->type == Tystruct && u != v) {
+ assert(u->nmemb = v->nmemb);
+ for (i = 0; i < v->nmemb; i++) {
+ assert(!strcmp(namestr(u->sdecls[i]->decl.name), namestr(v->sdecls[i]->decl.name)));
+ unify(st, u->sdecls[i], type(st, u->sdecls[i]), type(st, v->sdecls[i]));
+ }
+ }
+}
+
+/* Unifies two types, or errors if the types are not unifiable. */
+static Type *unify(Inferstate *st, Node *ctx, Type *u, Type *v)
+{
+ Type *t, *r;
+ Type *a, *b;
+ char *from, *to;
+ char buf[256];
+ size_t i;
+
+ /* a ==> b */
+ a = tf(st, u);
+ b = tf(st, v);
+ if (a == b)
+ return a;
+
+ /* we unify from lower to higher ranked types */
+ if (tyrank(b) < tyrank(a)) {
+ t = a;
+ a = b;
+ b = t;
+ }
+
+ if (debugopt['u']) {
+ from = tystr(a);
+ to = tystr(b);
+ printf("Unify %s => %s\n", from, to);
+ free(from);
+ free(to);
+ }
+
+ r = NULL;
+ if (a->type == Tyvar) {
+ tytab[a->tid] = b;
+ r = b;
+ }
+
+ /* Disallow recursive types */
+ if (a->type == Tyvar && b->type != Tyvar) {
+ if (occurs(a, b))
+ typeerror(st, a, b, ctx, "Infinite type\n");
+ }
+
+ /* if the tyrank of a is 0 (ie, a raw tyvar), just unify.
+ * Otherwise, match up subtypes. */
+ if ((a->type == b->type || idxhacked(a, b)) && tyrank(a) != 0) {
+ if (a->type == Tyname && !nameeq(a->name, b->name))
+ typeerror(st, a, b, ctx, NULL);
+ if (a->nsub != b->nsub) {
+ snprintf(buf, sizeof buf, "Wrong subtype count - Got %zu, expected %zu", a->nsub, b->nsub);
+ typeerror(st, a, b, ctx, buf);
+ }
+ for (i = 0; i < b->nsub; i++)
+ unify(st, ctx, a->sub[i], b->sub[i]);
+ r = b;
+ } else if (hasparam(a) && hasparam(b)) {
+ /* Only Tygeneric and Tyname should be able to unify. And they
+ * should have the same names for this to be true. */
+ if (!nameeq(a->name, b->name))
+ typeerror(st, a, b, ctx, NULL);
+ if (a->narg != b->narg)
+ typeerror(st, a, b, ctx, "Incompatible parameter lists");
+ for (i = 0; i < a->narg; i++)
+ unify(st, ctx, a->arg[i], b->arg[i]);
+ } else if (a->type != Tyvar) {
+ typeerror(st, a, b, ctx, NULL);
+ }
+ mergetraits(st, ctx, a, b);
+ membunify(st, ctx, a, b);
+
+ /* if we have delayed types for a tyvar, transfer it over. */
+ if (a->type == Tyvar && b->type == Tyvar) {
+ if (hthas(st->delayed, a) && !hthas(st->delayed, b))
+ htput(st->delayed, b, htget(st->delayed, a));
+ else if (hthas(st->delayed, b) && !hthas(st->delayed, a))
+ htput(st->delayed, a, htget(st->delayed, b));
+ } else if (hthas(st->delayed, a)) {
+ unify(st, ctx, htget(st->delayed, a), tybase(b));
+ }
+
+ return r;
+}
+
+/* Applies unifications to function calls.
+ * Funciton application requires a slightly
+ * different approach to unification. */
+static void unifycall(Inferstate *st, Node *n)
+{
+ size_t i;
+ Type *ft;
+
+ ft = type(st, n->expr.args[0]);
+ if (ft->type == Tyvar) {
+ /* the first arg is the function itself, so it shouldn't be counted */
+ ft = mktyfunc(n->line, &n->expr.args[1], n->expr.nargs - 1, mktyvar(n->line));
+ unify(st, n, ft, type(st, n->expr.args[0]));
+ }
+ for (i = 1; i < n->expr.nargs; i++) {
+ if (i == ft->nsub)
+ fatal(n->line, "%s arity mismatch (expected %zd args, got %zd)",
+ ctxstr(st, n->expr.args[0]), ft->nsub - 1, n->expr.nargs - 1);
+
+ if (ft->sub[i]->type == Tyvalist)
+ break;
+ inferexpr(st, n->expr.args[i], NULL, NULL);
+ unify(st, n->expr.args[0], ft->sub[i], type(st, n->expr.args[i]));
+ }
+ if (i < ft->nsub && ft->sub[i]->type != Tyvalist)
+ fatal(n->line, "%s arity mismatch (expected %zd args, got %zd)",
+ ctxstr(st, n->expr.args[0]), ft->nsub - 1, i - 1);
+ settype(st, n, ft->sub[0]);
+}
+
+static void unifyparams(Inferstate *st, Node *ctx, Type *a, Type *b)
+{
+ size_t i;
+
+ /* The only types with unifiable params are Tyunres and Tyname.
+ * Tygeneric should always be freshened, and no other types have
+ * parameters attached.
+ *
+ * FIXME: Is it possible to have parameterized typarams? */
+ if (a->type != Tyunres && a->type != Tyname)
+ return;
+ if (b->type != Tyunres && b->type != Tyname)
+ return;
+
+ if (a->narg != b->narg)
+ fatal(ctx->line, "Mismatched parameter list sizes: %s with %s near %s", tystr(a), tystr(b), ctxstr(st, ctx));
+ for (i = 0; i < a->narg; i++)
+ unify(st, ctx, a->arg[i], b->arg[i]);
+}
+
+static void loaduses(Node *n)
+{
+ size_t i;
+
+ /* uses only allowed at top level. Do we want to keep it this way? */
+ for (i = 0; i < n->file.nuses; i++)
+ readuse(n->file.uses[i], n->file.globls);
+}
+
+static void fiximpls(Inferstate *st, Stab *s)
+{
+ Node *n;
+ void **k;
+ size_t nk, i;
+
+ k = htkeys(s->impl, &nk);
+ for (i = 0; i < nk; i++) {
+ n = getimpl(s, k[i]);
+ htdel(s->impl, k[i]);
+ n->impl.type = tf(st, n->impl.type);
+ putimpl(s, n);
+ }
+ free(k);
+}
+
+/* The exports in package declarations
+ * need to be merged with the declarations
+ * at the global scope. Declarations in
+ * one may set the type of the other,
+ * so this should be done early in the
+ * process */
+static void mergeexports(Inferstate *st, Node *file)
+{
+ Stab *exports, *globls;
+ size_t i, nk;
+ void **k;
+ /* export, global version */
+ Node *nx, *ng;
+ Type *tx, *tg;
+ Trait *trx, *trg;
+ Ucon *ux, *ug;
+
+ exports = file->file.exports;
+ globls = file->file.globls;
+
+ /* export the types */
+ pushstab(globls);
+ k = htkeys(exports->ty, &nk);
+ for (i = 0; i < nk; i++) {
+ tx = gettype(exports, k[i]);
+ nx = k[i];
+ if (tx) {
+ tg = gettype(globls, nx);
+ if (!tg)
+ puttype(globls, nx, tx);
+ else
+ fatal(nx->line, "Exported type %s already declared on line %d", namestr(nx), tg->line);
+ } else {
+ tg = gettype(globls, nx);
+ if (tg)
+ updatetype(exports, nx, tf(st, tg));
+ else
+ fatal(nx->line, "Exported type %s not declared", namestr(nx));
+ }
+ }
+ free(k);
+
+ /* export the traits */
+ k = htkeys(exports->tr, &nk);
+ for (i = 0; i < nk; i++) {
+ trx = gettrait(exports, k[i]);
+ nx = k[i];
+ if (!trx->isproto) {
+ trg = gettrait(globls, nx);
+ if (!trg)
+ puttrait(globls, nx, trx);
+ else
+ fatal(nx->line, "Exported trait %s already declared on line %d", namestr(nx), trg->name->line);
+ } else {
+ trg = gettrait(globls, nx);
+ if (trg && !trg->isproto) {
+ *trx = *trg;
+ } else {
+ fatal(nx->line, "Exported trait %s not declared", namestr(nx));
+ }
+ }
+ trx->vis = Visexport;
+ }
+ free(k);
+
+ /*
+ * if we neglect to fix the types for impls before
+ * lookups, getimpl() on the global with the key from
+ * the export table will fail.
+ */
+ fiximpls(st, exports);
+ fiximpls(st, globls);
+
+ /* export the impls */
+ k = htkeys(exports->impl, &nk);
+ for (i = 0; i < nk; i++) {
+ nx = getimpl(exports, k[i]);
+ ng = getimpl(globls, k[i]);
+
+ if (nx->impl.isproto) {
+ if (!ng)
+ fatal(nx->line, "Missing trait impl body for %s %s\n", namestr(nx->impl.traitname), tystr(nx->impl.type));
+ htdel(exports->impl, k[i]);
+ putimpl(exports, ng);
+ ng->impl.vis = Visexport;
+ } else {
+ if (!ng) {
+ putimpl(globls, nx);
+ } else {
+ fatal(nx->line, "Double trait impl body for %s %s on line %d\n",
+ namestr(nx->impl.traitname), tystr(nx->impl.type), ng->line);
+ }
+ }
+ lappend(&exportimpls, &nexportimpls, ng);
+ }
+ free(k);
+
+ /* export the declarations */
+ k = htkeys(exports->dcl, &nk);
+ for (i = 0; i < nk; i++) {
+ nx = getdcl(exports, k[i]);
+ ng = getdcl(globls, k[i]);
+ /* if an export has an initializer, it shouldn't be declared in the
+ * body */
+ if (nx->decl.init && ng)
+ fatal(nx->line, "Export %s double-defined on line %d", ctxstr(st, nx), ng->line);
+ if (ng && nx->decl.isgeneric != ng->decl.isgeneric)
+ fatal(nx->line, "Export %s defined with different genericness on line %d", ctxstr(st, nx), ng->line);
+ if (!ng)
+ putdcl(globls, nx);
+ else
+ unify(st, nx, type(st, ng), type(st, nx));
+ }
+ free(k);
+
+
+
+ /* export the union constructors */
+ k = htkeys(exports->uc, &nk);
+ for (i = 0; i < nk; i++) {
+ ux = getucon(exports, k[i]);
+ ug = getucon(globls, k[i]);
+ /* if an export has an initializer, it shouldn't be declared in the
+ * body */
+ if (ux && ug)
+ fatal(ux->line, "Union constructor double defined on %d", ux->line);
+ else if (!ug)
+ putucon(globls, ux);
+ else
+ putucon(exports, ug);
+ }
+ free(k);
+
+ popstab();
+}
+
+static Type *initvar(Inferstate *st, Node *n, Node *s)
+{
+ Type *t;
+
+ if (s->decl.ishidden)
+ fatal(n->line, "attempting to refer to hidden decl %s", ctxstr(st, n));
+ if (s->decl.isgeneric)
+ t = tyfreshen(st, tf(st, s->decl.type));
+ else
+ t = s->decl.type;
+ settype(st, n, t);
+ n->expr.did = s->decl.did;
+ n->expr.isconst = s->decl.isconst;
+ if (s->decl.isgeneric && !st->ingeneric) {
+ lappend(&st->specializationscope, &st->nspecializationscope, curstab());
+ lappend(&st->specializations, &st->nspecializations, n);
+ lappend(&st->genericdecls, &st->ngenericdecls, s);
+ }
+ return t;
+}
+
+/* Finds out if the member reference is actually
+ * referring to a namespaced name, instead of a struct
+ * member. If it is, it transforms it into the variable
+ * reference we should have, instead of the Omemb expr
+ * that we do have */
+static void checkns(Inferstate *st, Node *n, Node **ret)
+{
+ Node *var, *name, *nsname;
+ Node **args;
+ Stab *stab;
+ Node *s;
+
+ /* check that this is a namespaced declaration */
+ if (n->type != Nexpr)
+ return;
+ if (!n->expr.nargs)
+ return;
+ args = n->expr.args;
+ if (args[0]->type != Nexpr || exprop(args[0]) != Ovar)
+ return;
+ name = args[0]->expr.args[0];
+ stab = getns(curstab(), name);
+ if (!stab)
+ return;
+
+ /* substitute the namespaced name */
+ nsname = mknsname(n->line, namestr(name), namestr(args[1]));
+ s = getdcl(stab, args[1]);
+ if (!s)
+ fatal(n->line, "Undeclared var %s.%s", nsname->name.ns, nsname->name.name);
+ var = mkexpr(n->line, Ovar, nsname, NULL);
+ initvar(st, var, s);
+ *ret = var;
+}
+
+static void inferstruct(Inferstate *st, Node *n, int *isconst)
+{
+ size_t i;
+
+ *isconst = 1;
+ for (i = 0; i < n->expr.nargs; i++) {
+ infernode(st, n->expr.args[i], NULL, NULL);
+ if (!n->expr.args[i]->expr.isconst)
+ *isconst = 0;
+ }
+ settype(st, n, mktyvar(n->line));
+ delayedcheck(st, n, curstab());
+}
+
+static void inferarray(Inferstate *st, Node *n, int *isconst)
+{
+ size_t i;
+ Type *t;
+ Node *len;
+
+ *isconst = 1;
+ len = mkintlit(n->line, n->expr.nargs);
+ t = mktyarray(n->line, mktyvar(n->line), len);
+ for (i = 0; i < n->expr.nargs; i++) {
+ infernode(st, n->expr.args[i], NULL, NULL);
+ unify(st, n, t->sub[0], type(st, n->expr.args[i]));
+ if (!n->expr.args[i]->expr.isconst)
+ *isconst = 0;
+ }
+ settype(st, n, t);
+}
+
+static void infertuple(Inferstate *st, Node *n, int *isconst)
+{
+ Type **types;
+ size_t i;
+
+ *isconst = 1;
+ types = xalloc(sizeof(Type *)*n->expr.nargs);
+ for (i = 0; i < n->expr.nargs; i++) {
+ infernode(st, n->expr.args[i], NULL, NULL);
+ n->expr.isconst = n->expr.isconst && n->expr.args[i]->expr.isconst;
+ types[i] = type(st, n->expr.args[i]);
+ }
+ *isconst = n->expr.isconst;
+ settype(st, n, mktytuple(n->line, types, n->expr.nargs));
+}
+
+static void inferucon(Inferstate *st, Node *n, int *isconst)
+{
+ Ucon *uc;
+ Type *t;
+
+ uc = uconresolve(st, n);
+ t = tyfreshen(st, tf(st, uc->utype));
+ uc = tybase(t)->udecls[uc->id];
+ if (uc->etype) {
+ inferexpr(st, n->expr.args[1], NULL, NULL);
+ unify(st, n, uc->etype, type(st, n->expr.args[1]));
+ }
+ *isconst = n->expr.args[0]->expr.isconst;
+ settype(st, n, delayeducon(st, t));
+}
+
+static void inferpat(Inferstate *st, Node *n, Node *val, Node ***bind, size_t *nbind)
+{
+ size_t i;
+ Node **args;
+ Node *s;
+ Type *t;
+
+ args = n->expr.args;
+ for (i = 0; i < n->expr.nargs; i++)
+ if (args[i]->type == Nexpr)
+ inferpat(st, args[i], val, bind, nbind);
+ switch (exprop(n)) {
+ case Otup:
+ case Ostruct:
+ case Oarr:
+ case Olit:
+ case Omemb:
+ infernode(st, n, NULL, NULL); break;
+ /* arithmetic expressions just need to be constant */
+ case Oneg:
+ case Oadd:
+ case Osub:
+ case Omul:
+ case Odiv:
+ case Obsl:
+ case Obsr:
+ case Oband:
+ case Obor:
+ case Obxor:
+ case Obnot:
+ infernode(st, n, NULL, NULL);
+ if (!n->expr.isconst)
+ fatal(n->line, "matching against non-constant expression");
+ break;
+ case Oucon: inferucon(st, n, &n->expr.isconst); break;
+ case Ovar:
+ s = getdcl(curstab(), args[0]);
+ if (s && !s->decl.ishidden) {
+ if (s->decl.isgeneric)
+ t = tyfreshen(st, s->decl.type);
+ else if (s->decl.isconst)
+ t = s->decl.type;
+ else
+ fatal(n->line, "Can't match against non-constant variables near %s", ctxstr(st, n));
+ } else {
+ t = mktyvar(n->line);
+ s = mkdecl(n->line, n->expr.args[0], t);
+ s->decl.init = val;
+ settype(st, n, t);
+ lappend(bind, nbind, s);
+ }
+ settype(st, n, t);
+ n->expr.did = s->decl.did;
+ break;
+ default:
+ fatal(n->line, "invalid pattern");
+ break;
+ }
+}
+
+void addbindings(Inferstate *st, Node *n, Node **bind, size_t nbind)
+{
+ size_t i;
+
+ /* order of binding shouldn't matter, so push them into the block
+ * in reverse order. */
+ for (i = 0; i < nbind; i++) {
+ putdcl(n->block.scope, bind[i]);
+ linsert(&n->block.stmts, &n->block.nstmts, 0, bind[i]);
+ }
+}
+
+static void infersub(Inferstate *st, Node *n, Type *ret, int *sawret, int *exprconst)
+{
+ Node **args;
+ size_t i, nargs;
+ int isconst;
+
+ args = n->expr.args;
+ nargs = n->expr.nargs;
+ isconst = 1;
+ for (i = 0; i < nargs; i++) {
+ /* Nlit, Nvar, etc should not be inferred as exprs */
+ if (args[i]->type == Nexpr) {
+ /* Omemb can sometimes resolve to a namespace. We have to check
+ * this. Icky. */
+ inferexpr(st, args[i], ret, sawret);
+ isconst = isconst && args[i]->expr.isconst;
+ }
+ }
+ if (ispureop[exprop(n)])
+ n->expr.isconst = isconst;
+ *exprconst = isconst;
+}
+
+static void inferexpr(Inferstate *st, Node *n, Type *ret, int *sawret)
+{
+ Node **args;
+ size_t i, nargs;
+ Node *s;
+ Type *t;
+ int isconst;
+
+ assert(n->type == Nexpr);
+ args = n->expr.args;
+ nargs = n->expr.nargs;
+ infernode(st, n->expr.idx, NULL, NULL);
+ for (i = 0; i < nargs; i++)
+ if (args[i]->type == Nexpr && exprop(args[i]) == Omemb)
+ checkns(st, args[i], &args[i]);
+ switch (exprop(n)) {
+ /* all operands are same type */
+ case Oadd: /* @a + @a -> @a */
+ case Osub: /* @a - @a -> @a */
+ case Omul: /* @a * @a -> @a */
+ case Odiv: /* @a / @a -> @a */
+ case Oneg: /* -@a -> @a */
+ infersub(st, n, ret, sawret, &isconst);
+ t = type(st, args[0]);
+ constrain(st, n, type(st, args[0]), traittab[Tcnum]);
+ isconst = args[0]->expr.isconst;
+ for (i = 1; i < nargs; i++) {
+ isconst = isconst && args[i]->expr.isconst;
+ t = unify(st, n, t, type(st, args[i]));
+ }
+ n->expr.isconst = isconst;
+ settype(st, n, t);
+ break;
+ case Omod: /* @a % @a -> @a */
+ case Obor: /* @a | @a -> @a */
+ case Oband: /* @a & @a -> @a */
+ case Obxor: /* @a ^ @a -> @a */
+ case Obsl: /* @a << @a -> @a */
+ case Obsr: /* @a >> @a -> @a */
+ case Obnot: /* ~@a -> @a */
+ case Opreinc: /* ++@a -> @a */
+ case Opredec: /* --@a -> @a */
+ case Opostinc: /* @a++ -> @a */
+ case Opostdec: /* @a-- -> @a */
+ case Oaddeq: /* @a += @a -> @a */
+ case Osubeq: /* @a -= @a -> @a */
+ case Omuleq: /* @a *= @a -> @a */
+ case Odiveq: /* @a /= @a -> @a */
+ case Omodeq: /* @a %= @a -> @a */
+ case Oboreq: /* @a |= @a -> @a */
+ case Obandeq: /* @a &= @a -> @a */
+ case Obxoreq: /* @a ^= @a -> @a */
+ case Obsleq: /* @a <<= @a -> @a */
+ case Obsreq: /* @a >>= @a -> @a */
+ infersub(st, n, ret, sawret, &isconst);
+ t = type(st, args[0]);
+ constrain(st, n, type(st, args[0]), traittab[Tcnum]);
+ constrain(st, n, type(st, args[0]), traittab[Tcint]);
+ isconst = args[0]->expr.isconst;
+ for (i = 1; i < nargs; i++) {
+ isconst = isconst && args[i]->expr.isconst;
+ t = unify(st, n, t, type(st, args[i]));
+ }
+ n->expr.isconst = isconst;
+ settype(st, n, t);
+ break;
+ case Oasn: /* @a = @a -> @a */
+ infersub(st, n, ret, sawret, &isconst);
+ t = type(st, args[0]);
+ for (i = 1; i < nargs; i++)
+ t = unify(st, n, t, type(st, args[i]));
+ settype(st, n, t);
+ if (args[0]->expr.isconst)
+ fatal(n->line, "Attempting to assign constant \"%s\"", ctxstr(st, args[0]));
+ break;
+
+ /* operands same type, returning bool */
+ case Olor: /* @a || @b -> bool */
+ case Oland: /* @a && @b -> bool */
+ case Olnot: /* !@a -> bool */
+ case Oeq: /* @a == @a -> bool */
+ case One: /* @a != @a -> bool */
+ case Ogt: /* @a > @a -> bool */
+ case Oge: /* @a >= @a -> bool */
+ case Olt: /* @a < @a -> bool */
+ case Ole: /* @a <= @b -> bool */
+ infersub(st, n, ret, sawret, &isconst);
+ t = type(st, args[0]);
+ for (i = 1; i < nargs; i++)
+ unify(st, n, t, type(st, args[i]));
+ settype(st, n, mktype(-1, Tybool));
+ break;
+
+ /* reach into a type and pull out subtypes */
+ case Oaddr: /* &@a -> @a* */
+ infersub(st, n, ret, sawret, &isconst);
+ settype(st, n, mktyptr(n->line, type(st, args[0])));
+ break;
+ case Oderef: /* *@a* -> @a */
+ infersub(st, n, ret, sawret, &isconst);
+ t = unify(st, n, type(st, args[0]), mktyptr(n->line, mktyvar(n->line)));
+ settype(st, n, t->sub[0]);
+ break;
+ case Oidx: /* @a[@b::tcint] -> @a */
+ infersub(st, n, ret, sawret, &isconst);
+ t = mktyidxhack(n->line, mktyvar(n->line));
+ unify(st, n, type(st, args[0]), t);
+ constrain(st, n, type(st, args[1]), traittab[Tcint]);
+ settype(st, n, t->sub[0]);
+ break;
+ case Oslice: /* @a[@b::tcint,@b::tcint] -> @a[,] */
+ infersub(st, n, ret, sawret, &isconst);
+ t = mktyidxhack(n->line, mktyvar(n->line));
+ unify(st, n, type(st, args[0]), t);
+ constrain(st, n, type(st, args[1]), traittab[Tcint]);
+ constrain(st, n, type(st, args[2]), traittab[Tcint]);
+ settype(st, n, mktyslice(n->line, t->sub[0]));
+ break;
+
+ /* special cases */
+ case Omemb: /* @a.Ident -> @b, verify type(@a.Ident)==@b later */
+ infersub(st, n, ret, sawret, &isconst);
+ settype(st, n, mktyvar(n->line));
+ delayedcheck(st, n, curstab());
+ break;
+ case Osize: /* sizeof @a -> size */
+ infersub(st, n, ret, sawret, &isconst);
+ settype(st, n, mktylike(n->line, Tyuint));
+ break;
+ case Ocall: /* (@a, @b, @c, ... -> @r)(@a,@b,@c, ... -> @r) -> @r */
+ infersub(st, n, ret, sawret, &isconst);
+ unifycall(st, n);
+ break;
+ case Ocast: /* cast(@a, @b) -> @b */
+ infersub(st, n, ret, sawret, &isconst);
+ delayedcheck(st, n, curstab());
+ break;
+ case Oret: /* -> @a -> void */
+ infersub(st, n, ret, sawret, &isconst);
+ if (sawret)
+ *sawret = 1;
+ if (!ret)
+ fatal(n->line, "Not allowed to return value here");
+ if (nargs)
+ t = unify(st, n, ret, type(st, args[0]));
+ else
+ t = unify(st, n, mktype(-1, Tyvoid), ret);
+ settype(st, n, t);
+ break;
+ case Obreak:
+ case Ocontinue:
+ /* nullary: nothing to infer. */
+ settype(st, n, mktype(-1, Tyvoid));
+ break;
+ case Ojmp: /* goto void* -> void */
+ infersub(st, n, ret, sawret, &isconst);
+ settype(st, n, mktype(-1, Tyvoid));
+ break;
+ case Ovar: /* a:@a -> @a */
+ infersub(st, n, ret, sawret, &isconst);
+ /* if we created this from a namespaced var, the type should be
+ * set, and the normal lookup is expected to fail. Since we're
+ * already done with this node, we can just return. */
+ if (n->expr.type)
+ return;
+ s = getdcl(curstab(), args[0]);
+ if (!s)
+ fatal(n->line, "Undeclared var %s", ctxstr(st, args[0]));
+ initvar(st, n, s);
+ break;
+ case Oucon:
+ inferucon(st, n, &n->expr.isconst);
+ break;
+ case Otup:
+ infertuple(st, n, &n->expr.isconst);
+ break;
+ case Ostruct:
+ inferstruct(st, n, &n->expr.isconst);
+ break;
+ case Oarr:
+ inferarray(st, n, &n->expr.isconst);
+ break;
+ case Olit: /* <lit>:@a::tyclass -> @a */
+ infersub(st, n, ret, sawret, &isconst);
+ switch (args[0]->lit.littype) {
+ case Lfunc:
+ infernode(st, args[0]->lit.fnval, NULL, NULL); break;
+ /* FIXME: env capture means this is non-const */
+ n->expr.isconst = 1;
+ default:
+ n->expr.isconst = 1;
+ break;
+ }
+ settype(st, n, type(st, args[0]));
+ break;
+ case Olbl: /* :lbl -> void* */
+ infersub(st, n, ret, sawret, &isconst);
+ settype(st, n, mktyptr(n->line, mktype(-1, Tyvoid)));
+ case Obad: case Ocjmp: case Oset:
+ case Oslbase: case Osllen:
+ case Oblit: case Numops:
+ case Otrunc: case Oswiden: case Ozwiden:
+ case Oint2flt: case Oflt2int:
+ case Ofadd: case Ofsub: case Ofmul: case Ofdiv: case Ofneg:
+ case Ofeq: case Ofne: case Ofgt: case Ofge: case Oflt: case Ofle:
+ case Oueq: case Oune: case Ougt: case Ouge: case Oult: case Oule:
+ case Ouget:
+ die("Should not see %s in fe", opstr(exprop(n)));
+ break;
+ }
+}
+
+static void inferfunc(Inferstate *st, Node *n)
+{
+ size_t i;
+ int sawret;
+
+ sawret = 0;
+ for (i = 0; i < n->func.nargs; i++)
+ infernode(st, n->func.args[i], NULL, NULL);
+ infernode(st, n->func.body, n->func.type->sub[0], &sawret);
+ /* if there's no return stmt in the function, assume void ret */
+ if (!sawret)
+ unify(st, n, type(st, n)->sub[0], mktype(-1, Tyvoid));
+}
+
+static void specializeimpl(Inferstate *st, Node *n)
+{
+ Node *dcl, *proto, *name;
+ Htab *ht;
+ Trait *t;
+ Type *ty;
+ size_t i, j;
+
+ t = gettrait(curstab(), n->impl.traitname);
+ if (!t)
+ fatal(n->line, "No trait %s\n", namestr(n->impl.traitname));
+ n->impl.trait = t;
+
+ dcl = NULL;
+ proto = NULL;
+ for (i = 0; i < n->impl.ndecls; i++) {
+ /* look up the prototype */
+ proto = NULL;
+ dcl = n->impl.decls[i];
+
+ /*
+ since the decls in an impl are not installed in a namespace, their names
+ are not updated when we call updatens() on the symbol table. Because we need
+ to do namespace dependent comparisons for specializing, we need to set the
+ namespace here.
+ */
+ if (file->file.globls->name)
+ setns(dcl->decl.name, namestr(file->file.globls->name));
+ for (j = 0; j < t->nfuncs; j++) {
+ if (nameeq(dcl->decl.name, t->funcs[j]->decl.name)) {
+ proto = t->funcs[j];
+ break;
+ }
+ }
+ if (!dcl || !proto)
+ fatal(n->line, "Declaration %s missing in %s, near %s\n",
+ namestr(dcl->decl.name), namestr(t->name), ctxstr(st, n));
+
+ /* infer and unify types */
+ checktraits(t->param, n->impl.type);
+ ht = mkht(tyhash, tyeq);
+ htput(ht, t->param, n->impl.type);
+ ty = tyspecialize(type(st, proto), ht);
+ htfree(ht);
+
+ inferdecl(st, dcl);
+ unify(st, n, type(st, dcl), ty);
+
+ /* and put the specialization into the global stab */
+ name = genericname(proto, ty);
+ dcl->decl.name = name;
+ putdcl(file->file.globls, dcl);
+ if (debugopt['S'])
+ printf("specializing trait [%d]%s:%s => %s:%s\n",
+ n->line, namestr(proto->decl.name), tystr(type(st, proto)), namestr(name), tystr(ty));
+ lappend(&file->file.stmts, &file->file.nstmts, dcl);
+ }
+}
+
+static void inferdecl(Inferstate *st, Node *n)
+{
+ Type *t;
+
+ t = tf(st, decltype(n));
+ if (t->type == Tyname && isgeneric(t) && !n->decl.isgeneric) {
+ t = tyfreshen(st, t);
+ unifyparams(st, n, t, decltype(n));
+ }
+ settype(st, n, t);
+ if (n->decl.init) {
+ inferexpr(st, n->decl.init, NULL, NULL);
+ unify(st, n, type(st, n), type(st, n->decl.init));
+ if (n->decl.isconst && !n->decl.init->expr.isconst)
+ fatal(n->line, "non-const initializer for \"%s\"", ctxstr(st, n));
+ } else {
+ if ((n->decl.isconst || n->decl.isgeneric) && !n->decl.isextern)
+ fatal(n->line, "non-extern \"%s\" has no initializer", ctxstr(st, n));
+ }
+}
+
+static void inferstab(Inferstate *st, Stab *s)
+{
+ void **k;
+ size_t n, i;
+ Type *t;
+
+ k = htkeys(s->ty, &n);
+ for (i = 0; i < n; i++) {
+ t = tysearch(st, gettype(s, k[i]));
+ updatetype(s, k[i], t);
+ }
+ free(k);
+}
+
+static void infernode(Inferstate *st, Node *n, Type *ret, int *sawret)
+{
+ size_t i, nbound;
+ Node **bound;
+ Node *d, *s;
+ Type *t;
+
+ if (!n)
+ return;
+ switch (n->type) {
+ case Nfile:
+ pushstab(n->file.globls);
+ inferstab(st, n->file.globls);
+ inferstab(st, n->file.exports);
+ for (i = 0; i < n->file.nstmts; i++) {
+ d = n->file.stmts[i];
+ infernode(st, d, NULL, sawret);
+ /* exports allow us to specify types later in the body, so we
+ * need to patch the types in if they don't have a definition */
+ if (d->type == Ndecl) {
+ s = getdcl(file->file.exports, d->decl.name);
+ if (s) {
+ d->decl.vis = Visexport;
+ unify(st, d, type(st, d), s->decl.type);
+ forcedcl(file->file.exports, d);
+ }
+ }
+ }
+ popstab();
+ break;
+ case Ndecl:
+ bind(st, n);
+ inferdecl(st, n);
+ if (type(st, n)->type == Typaram && !st->ingeneric)
+ fatal(n->line, "Generic type %s in non-generic near %s\n", tystr(type(st, n)), ctxstr(st, n));
+ unbind(st, n);
+ break;
+ case Nblock:
+ setsuper(n->block.scope, curstab());
+ pushstab(n->block.scope);
+ inferstab(st, n->block.scope);
+ for (i = 0; i < n->block.nstmts; i++) {
+ checkns(st, n->block.stmts[i], &n->block.stmts[i]);
+ infernode(st, n->block.stmts[i], ret, sawret);
+ }
+ popstab();
+ break;
+ case Nifstmt:
+ infernode(st, n->ifstmt.cond, NULL, sawret);
+ infernode(st, n->ifstmt.iftrue, ret, sawret);
+ infernode(st, n->ifstmt.iffalse, ret, sawret);
+ unify(st, n, type(st, n->ifstmt.cond), mktype(n->line, Tybool));
+ break;
+ case Nloopstmt:
+ infernode(st, n->loopstmt.init, ret, sawret);
+ infernode(st, n->loopstmt.cond, NULL, sawret);
+ infernode(st, n->loopstmt.step, ret, sawret);
+ infernode(st, n->loopstmt.body, ret, sawret);
+ unify(st, n, type(st, n->loopstmt.cond), mktype(n->line, Tybool));
+ break;
+ case Niterstmt:
+ bound = NULL;
+ nbound = 0;
+
+ inferpat(st, n->iterstmt.elt, NULL, &bound, &nbound);
+ addbindings(st, n->iterstmt.body, bound, nbound);
+
+ infernode(st, n->iterstmt.seq, NULL, sawret);
+ infernode(st, n->iterstmt.body, ret, sawret);
+
+ t = mktyidxhack(n->line, mktyvar(n->line));
+ constrain(st, n, type(st, n->iterstmt.seq), traittab[Tcidx]);
+ unify(st, n, type(st, n->iterstmt.seq), t);
+ unify(st, n, type(st, n->iterstmt.elt), t->sub[0]);
+ break;
+ case Nmatchstmt:
+ infernode(st, n->matchstmt.val, NULL, sawret);
+ if (tybase(type(st, n->matchstmt.val))->type == Tyvoid)
+ fatal(n->line, "Can't match against a void type near %s", ctxstr(st, n->matchstmt.val));
+ for (i = 0; i < n->matchstmt.nmatches; i++) {
+ infernode(st, n->matchstmt.matches[i], ret, sawret);
+ unify(st, n, type(st, n->matchstmt.val), type(st, n->matchstmt.matches[i]->match.pat));
+ }
+ break;
+ case Nmatch:
+ bound = NULL;
+ nbound = 0;
+ inferpat(st, n->match.pat, NULL, &bound, &nbound);
+ addbindings(st, n->match.block, bound, nbound);
+ infernode(st, n->match.block, ret, sawret);
+ break;
+ case Nexpr:
+ inferexpr(st, n, ret, sawret);
+ break;
+ case Nfunc:
+ setsuper(n->func.scope, curstab());
+ if (st->ntybindings > 0)
+ for (i = 0; i < n->func.nargs; i++)
+ putbindings(st, st->tybindings[st->ntybindings - 1], n->func.args[i]->decl.type);
+ pushstab(n->func.scope);
+ inferstab(st, n->block.scope);
+ inferfunc(st, n);
+ popstab();
+ break;
+ case Nimpl:
+ specializeimpl(st, n);
+ break;
+ case Nname:
+ case Nlit:
+ case Nuse:
+ break;
+ case Nnone:
+ die("Nnone should not be seen as node type!");
+ break;
+ }
+}
+
+/* returns the final type for t, after all unifications
+ * and default constraint selections */
+static Type *tyfix(Inferstate *st, Node *ctx, Type *orig)
+{
+ static Type *tyint, *tyflt;
+ Type *t, *delayed;
+ size_t i;
+ char buf[1024];
+
+ if (!tyint)
+ tyint = mktype(-1, Tyint);
+ if (!tyflt)
+ tyflt = mktype(-1, Tyfloat64);
+
+ t = tysearch(st, orig);
+ if (orig->type == Tyvar && hthas(st->delayed, orig)) {
+ delayed = htget(st->delayed, orig);
+ if (t->type == Tyvar)
+ t = delayed;
+ else if (tybase(t)->type != delayed->type)
+ fatal(ctx->line, "Type %s not compatible with %s near %s\n", tystr(t), tystr(delayed), ctxstr(st, ctx));
+ }
+ if (t->type == Tyvar) {
+ if (hastrait(t, traittab[Tcint]) && checktraits(t, tyint))
+ return tyint;
+ if (hastrait(t, traittab[Tcfloat]) && checktraits(t, tyflt))
+ return tyflt;
+ } else if (!t->fixed) {
+ t->fixed = 1;
+ if (t->type == Tyarray) {
+ typesub(st, t->asize);
+ } else if (t->type == Tystruct) {
+ for (i = 0; i < t->nmemb; i++)
+ typesub(st, t->sdecls[i]);
+ } else if (t->type == Tyunion) {
+ for (i = 0; i < t->nmemb; i++) {
+ if (t->udecls[i]->etype)
+ t->udecls[i]->etype = tyfix(st, ctx, t->udecls[i]->etype);
+ }
+ } else if (t->type == Tyname) {
+ for (i = 0; i < t->narg; i++)
+ t->arg[i] = tyfix(st, ctx, t->arg[i]);
+ }
+ for (i = 0; i < t->nsub; i++)
+ t->sub[i] = tyfix(st, ctx, t->sub[i]);
+ }
+ if (t->type == Tyvar) {
+ if (debugopt['T'])
+ dump(file, stdout);
+ fatal(t->line, "underconstrained type %s near %s", tyfmt(buf, 1024, t), ctxstr(st, ctx));
+ }
+
+ return t;
+}
+
+static void checkcast(Inferstate *st, Node *n)
+{
+ /* FIXME: actually verify the casts. Right now, it's ok to leave this
+ * unimplemented because bad casts get caught by the backend. */
+}
+
+static void infercompn(Inferstate *st, Node *n)
+{
+ Node *aggr;
+ Node *memb;
+ Node **nl;
+ Type *t;
+ size_t i;
+ int found;
+
+ aggr = n->expr.args[0];
+ memb = n->expr.args[1];
+
+ found = 0;
+ t = tybase(tf(st, type(st, aggr)));
+ /* all array-like types have a fake "len" member that we emulate */
+ if (t->type == Tyslice || t->type == Tyarray) {
+ if (!strcmp(namestr(memb), "len")) {
+ constrain(st, n, type(st, n), traittab[Tcnum]);
+ constrain(st, n, type(st, n), traittab[Tcint]);
+ found = 1;
+ }
+ /* otherwise, we search aggregate types for the member, and unify
+ * the expression with the member type; ie:
+ *
+ * x: aggrtype y : memb in aggrtype
+ * ---------------------------------------
+ * x.y : membtype
+ */
+ } else {
+ if (t->type == Typtr)
+ t = tybase(tf(st, t->sub[0]));
+ nl = t->sdecls;
+ for (i = 0; i < t->nmemb; i++) {
+ if (!strcmp(namestr(memb), declname(nl[i]))) {
+ unify(st, n, type(st, n), decltype(nl[i]));
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (!found)
+ fatal(aggr->line, "Type %s has no member \"%s\" near %s",
+ tystr(type(st, aggr)), ctxstr(st, memb), ctxstr(st, aggr));
+}
+
+static void checkstruct(Inferstate *st, Node *n)
+{
+ Type *t, *et;
+ Node *val, *name;
+ size_t i, j;
+
+ t = tybase(tf(st, n->lit.type));
+ if (t->type != Tystruct)
+ fatal(n->line, "Type %s for struct literal is not struct near %s", tystr(t), ctxstr(st, n));
+
+ for (i = 0; i < n->expr.nargs; i++) {
+ val = n->expr.args[i];
+ name = val->expr.idx;
+
+ et = NULL;
+ for (j = 0; j < t->nmemb; j++) {
+ if (!strcmp(namestr(t->sdecls[j]->decl.name), namestr(name))) {
+ et = type(st, t->sdecls[j]);
+ break;
+ }
+ }
+
+ if (!et)
+ fatal(n->line, "Could not find member %s in struct %s, near %s",
+ namestr(name), tystr(t), ctxstr(st, n));
+
+ unify(st, val, et, type(st, val));
+ }
+}
+
+static void postcheck(Inferstate *st, Node *file)
+{
+ size_t i;
+ Node *n;
+
+ for (i = 0; i < st->npostcheck; i++) {
+ n = st->postcheck[i];
+ pushstab(st->postcheckscope[i]);
+ if (n->type == Nexpr && exprop(n) == Omemb)
+ infercompn(st, n);
+ else if (n->type == Nexpr && exprop(n) == Ocast)
+ checkcast(st, n);
+ else if (n->type == Nexpr && exprop(n) == Ostruct)
+ checkstruct(st, n);
+ else
+ die("Thing we shouldn't be checking in postcheck\n");
+ popstab();
+ }
+}
+
+/* After inference, replace all
+ * types in symbol tables with
+ * the final computed types */
+static void stabsub(Inferstate *st, Stab *s)
+{
+ void **k;
+ size_t n, i;
+ Type *t;
+ Node *d;
+
+ k = htkeys(s->ty, &n);
+ for (i = 0; i < n; i++) {
+ t = tysearch(st, gettype(s, k[i]));
+ updatetype(s, k[i], t);
+ tyfix(st, k[i], t);
+ }
+ free(k);
+
+ k = htkeys(s->dcl, &n);
+ for (i = 0; i < n; i++) {
+ d = getdcl(s, k[i]);
+ if (d)
+ d->decl.type = tyfix(st, d, d->decl.type);
+ }
+ free(k);
+}
+
+static void checkrange(Inferstate *st, Node *n)
+{
+ Type *t;
+ int64_t sval;
+ uint64_t uval;
+ static const int64_t svranges[][2] = {
+ /* signed ints */
+ [Tyint8] = {-128LL, 127LL},
+ [Tyint16] = {-32768LL, 32767LL},
+ [Tyint32] = {-2147483648LL, 2*2147483647LL}, /* FIXME: this has been doubled allow for uints... */
+ [Tyint] = {-2147483648LL, 2*2147483647LL},
+ [Tyint64] = {-9223372036854775808ULL, 9223372036854775807LL},
+ [Tylong] = {-9223372036854775808ULL, 9223372036854775807LL},
+ };
+
+ static const uint64_t uvranges[][2] = {
+ [Tybyte] = {0, 255ULL},
+ [Tyuint8] = {0, 255ULL},
+ [Tyuint16] = {0, 65535ULL},
+ [Tyuint32] = {0, 4294967295ULL},
+ [Tyuint64] = {0, 18446744073709551615ULL},
+ [Tyulong] = {0, 18446744073709551615ULL},
+ [Tychar] = {0, 4294967295ULL},
+ };
+
+ /* signed types */
+ t = type(st, n);
+ if (t->type >= Tyint8 && t->type <= Tylong) {
+ sval = n->lit.intval;
+ if (sval < svranges[t->type][0] || sval > svranges[t->type][1])
+ fatal(n->line, "Literal value %lld out of range for type \"%s\"", sval, tystr(t));
+ } else if ((t->type >= Tybyte && t->type <= Tyulong) || t->type == Tychar) {
+ uval = n->lit.intval;
+ if (uval < uvranges[t->type][0] || uval > uvranges[t->type][1])
+ fatal(n->line, "Literal value %llu out of range for type \"%s\"", uval, tystr(t));
+ }
+}
+
+/* After type inference, replace all types
+ * with the final computed type */
+static void typesub(Inferstate *st, Node *n)
+{
+ size_t i;
+
+ if (!n)
+ return;
+ switch (n->type) {
+ case Nfile:
+ pushstab(n->file.globls);
+ stabsub(st, n->file.globls);
+ stabsub(st, n->file.exports);
+ for (i = 0; i < n->file.nstmts; i++)
+ typesub(st, n->file.stmts[i]);
+ popstab();
+ break;
+ case Ndecl:
+ settype(st, n, tyfix(st, n, type(st, n)));
+ if (n->decl.init)
+ typesub(st, n->decl.init);
+ break;
+ case Nblock:
+ pushstab(n->block.scope);
+ for (i = 0; i < n->block.nstmts; i++)
+ typesub(st, n->block.stmts[i]);
+ popstab();
+ break;
+ case Nifstmt:
+ typesub(st, n->ifstmt.cond);
+ typesub(st, n->ifstmt.iftrue);
+ typesub(st, n->ifstmt.iffalse);
+ break;
+ case Nloopstmt:
+ typesub(st, n->loopstmt.cond);
+ typesub(st, n->loopstmt.init);
+ typesub(st, n->loopstmt.step);
+ typesub(st, n->loopstmt.body);
+ break;
+ case Niterstmt:
+ typesub(st, n->iterstmt.elt);
+ typesub(st, n->iterstmt.seq);
+ typesub(st, n->iterstmt.body);
+ break;
+ case Nmatchstmt:
+ typesub(st, n->matchstmt.val);
+ for (i = 0; i < n->matchstmt.nmatches; i++) {
+ typesub(st, n->matchstmt.matches[i]);
+ }
+ break;
+ case Nmatch:
+ typesub(st, n->match.pat);
+ typesub(st, n->match.block);
+ break;
+ case Nexpr:
+ settype(st, n, tyfix(st, n, type(st, n)));
+ typesub(st, n->expr.idx);
+ if (exprop(n) == Ocast && exprop(n->expr.args[0]) == Olit && n->expr.args[0]->expr.args[0]->lit.littype == Lint) {
+ settype(st, n->expr.args[0], exprtype(n));
+ settype(st, n->expr.args[0]->expr.args[0], exprtype(n));
+ }
+ for (i = 0; i < n->expr.nargs; i++)
+ typesub(st, n->expr.args[i]);
+ break;
+ case Nfunc:
+ pushstab(n->func.scope);
+ settype(st, n, tyfix(st, n, n->func.type));
+ for (i = 0; i < n->func.nargs; i++)
+ typesub(st, n->func.args[i]);
+ typesub(st, n->func.body);
+ popstab();
+ break;
+ case Nlit:
+ settype(st, n, tyfix(st, n, type(st, n)));
+ switch (n->lit.littype) {
+ case Lfunc:
+ typesub(st, n->lit.fnval); break;
+ case Lint:
+ checkrange(st, n);
+ default: break;
+ }
+ break;
+ case Nimpl:
+ case Nname:
+ case Nuse:
+ break;
+ case Nnone:
+ die("Nnone should not be seen as node type!");
+ break;
+ }
+}
+
+static void taghidden(Type *t)
+{
+ size_t i;
+
+ if (t->vis != Visintern)
+ return;
+ t->vis = Vishidden;
+ for (i = 0; i < t->nsub; i++)
+ taghidden(t->sub[i]);
+ switch (t->type) {
+ case Tystruct:
+ for (i = 0; i < t->nmemb; i++)
+ taghidden(decltype(t->sdecls[i]));
+ break;
+ case Tyunion:
+ for (i = 0; i < t->nmemb; i++)
+ if (t->udecls[i]->etype)
+ taghidden(t->udecls[i]->etype);
+ break;
+ case Tyname:
+ for (i = 0; i < t->narg; i++)
+ taghidden(t->arg[i]);
+ for (i = 0; i < t->nparam; i++)
+ taghidden(t->param[i]);
+ break;
+ default:
+ break;
+ }
+}
+
+static void nodetag(Stab *st, Node *n, int ingeneric)
+{
+ size_t i;
+ Node *d;
+
+ if (!n)
+ return;
+ switch (n->type) {
+ case Nblock:
+ for (i = 0; i < n->block.nstmts; i++)
+ nodetag(st, n->block.stmts[i], ingeneric);
+ break;
+ case Nifstmt:
+ nodetag(st, n->ifstmt.cond, ingeneric);
+ nodetag(st, n->ifstmt.iftrue, ingeneric);
+ nodetag(st, n->ifstmt.iffalse, ingeneric);
+ break;
+ case Nloopstmt:
+ nodetag(st, n->loopstmt.init, ingeneric);
+ nodetag(st, n->loopstmt.cond, ingeneric);
+ nodetag(st, n->loopstmt.step, ingeneric);
+ nodetag(st, n->loopstmt.body, ingeneric);
+ break;
+ case Niterstmt:
+ nodetag(st, n->iterstmt.elt, ingeneric);
+ nodetag(st, n->iterstmt.seq, ingeneric);
+ nodetag(st, n->iterstmt.body, ingeneric);
+ break;
+ case Nmatchstmt:
+ nodetag(st, n->matchstmt.val, ingeneric);
+ for (i = 0; i < n->matchstmt.nmatches; i++)
+ nodetag(st, n->matchstmt.matches[i], ingeneric);
+ break;
+ case Nmatch:
+ nodetag(st, n->match.pat, ingeneric);
+ nodetag(st, n->match.block, ingeneric);
+ break;
+ case Nexpr:
+ nodetag(st, n->expr.idx, ingeneric);
+ taghidden(n->expr.type);
+ for (i = 0; i < n->expr.nargs; i++)
+ nodetag(st, n->expr.args[i], ingeneric);
+ /* generics need to have the decls they refer to exported. */
+ if (ingeneric && exprop(n) == Ovar) {
+ d = decls[n->expr.did];
+ if (d->decl.isglobl && d->decl.vis == Visintern) {
+ d->decl.vis = Vishidden;
+ putdcl(st, d);
+ nodetag(st, d, ingeneric);
+ }
+ }
+ break;
+ case Nlit:
+ taghidden(n->lit.type);
+ if (n->lit.littype == Lfunc)
+ nodetag(st, n->lit.fnval, ingeneric);
+ break;
+ case Ndecl:
+ taghidden(n->decl.type);
+ /* generics export their body. */
+ if (n->decl.isgeneric)
+ nodetag(st, n->decl.init, n->decl.isgeneric);
+ break;
+ case Nfunc:
+ taghidden(n->func.type);
+ for (i = 0; i < n->func.nargs; i++)
+ nodetag(st, n->func.args[i], ingeneric);
+ nodetag(st, n->func.body, ingeneric);
+ break;
+ case Nimpl:
+ for (i = 0; i < n->impl.ndecls; i++) {
+ n->impl.decls[i]->decl.vis = Vishidden;
+ nodetag(st, n->impl.decls[i], 0);
+ }
+ break;
+ case Nuse: case Nname:
+ break;
+ case Nfile: case Nnone:
+ die("Invalid node for type export\n");
+ break;
+ }
+}
+
+void tagexports(Stab *st)
+{
+ void **k;
+ Node *s;
+ Type *t;
+ size_t i, j, n;
+
+ k = htkeys(st->dcl, &n);
+ for (i = 0; i < n; i++) {
+ s = getdcl(st, k[i]);
+ nodetag(st, s, 0);
+ }
+ free(k);
+
+ for (i = 0; i < nexportimpls; i++) {
+ nodetag(st, exportimpls[i], 0);
+ }
+
+ /* get the explicitly exported symbols */
+ k = htkeys(st->ty, &n);
+ for (i = 0; i < n; i++) {
+ t = gettype(st, k[i]);
+ t->vis = Visexport;
+ taghidden(t);
+ for (j = 0; j < t->nsub; j++)
+ taghidden(t->sub[j]);
+ for (j = 0; j < t->narg; j++)
+ taghidden(t->arg[j]);
+ for (j = 0; j < t->nparam; j++)
+ taghidden(t->param[j]);
+ }
+ free(k);
+
+}
+
+/* Take generics and build new versions of them
+ * with the type parameters replaced with the
+ * specialized types */
+static void specialize(Inferstate *st, Node *f)
+{
+ Node *d, *name;
+ size_t i;
+
+ for (i = 0; i < st->nspecializations; i++) {
+ pushstab(st->specializationscope[i]);
+ d = specializedcl(st->genericdecls[i], st->specializations[i]->expr.type, &name);
+ st->specializations[i]->expr.args[0] = name;
+ st->specializations[i]->expr.did = d->decl.did;
+
+ /* we need to sub in default types in the specialization, so call
+ * typesub on the specialized function */
+ typesub(st, d);
+ popstab();
+ }
+}
+
+void applytraits(Inferstate *st, Node *f)
+{
+ size_t i;
+ Node *n;
+ Trait *trait;
+ Type *ty;
+
+ pushstab(f->file.globls);
+ /* for now, traits can only be declared globally */
+ for (i = 0; i < f->file.nstmts; i++) {
+ if (f->file.stmts[i]->type == Nimpl) {
+ n = f->file.stmts[i];
+ trait = gettrait(f->file.globls, n->impl.traitname);
+ if (!trait)
+ fatal(n->line, "trait %s does not exist near %s", namestr(n->impl.traitname), ctxstr(st, n));
+ ty = tf(st, n->impl.type);
+ settrait(ty, trait);
+ }
+ }
+ popstab();
+}
+
+void infer(Node *file)
+{
+ Inferstate st = {0,};
+
+ assert(file->type == Nfile);
+ st.delayed = mkht(tyhash, tyeq);
+ /* set up the symtabs */
+ loaduses(file);
+ mergeexports(&st, file);
+
+ /* do the inference */
+ applytraits(&st, file);
+ infernode(&st, file, NULL, NULL);
+ postcheck(&st, file);
+
+ /* and replace type vars with actual types */
+ typesub(&st, file);
+ specialize(&st, file);
+ tagexports(file->file.exports);
+}
--- /dev/null
+++ b/parse/lits.def
@@ -1,0 +1,7 @@
+L(Lchr)
+L(Lbool)
+L(Lint)
+L(Lflt)
+L(Lstr)
+L(Lfunc)
+L(Llbl)
--- /dev/null
+++ b/parse/names.c
@@ -1,0 +1,62 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+
+static char *optab[] = {
+#define O(op, pure) #op,
+#include "ops.def"
+#undef O
+};
+
+int ispureop[] = {
+#define O(op, pure) pure,
+#include "ops.def"
+#undef O
+};
+
+static char *nodetab[] = {
+#define N(nt) #nt,
+#include "nodes.def"
+#undef N
+};
+
+static char *littab[] = {
+#define L(lt) #lt,
+#include "lits.def"
+#undef L
+};
+
+static char *tidtab[] = {
+#define Ty(t, n) n,
+#include "types.def"
+#undef Ty
+};
+
+char *opstr(Op o)
+{
+ return optab[o];
+}
+
+char *nodestr(Ntype nt)
+{
+ return nodetab[nt];
+}
+
+char *litstr(Littype lt)
+{
+ return littab[lt];
+}
+
+char *tidstr(Ty tid)
+{
+ return tidtab[tid];
+}
--- /dev/null
+++ b/parse/node.c
@@ -1,0 +1,417 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+
+size_t maxnid;
+Node **decls;
+size_t ndecls;
+Node **exportimpls;
+size_t nexportimpls;
+
+Node *mknode(int line, Ntype nt)
+{
+ Node *n;
+
+ n = zalloc(sizeof(Node));
+ n->nid = maxnid++;
+ n->type = nt;
+ n->line = line;
+ return n;
+}
+
+Node *mkfile(char *name)
+{
+ Node *n;
+
+ n = mknode(-1, Nfile);
+ n->file.name = strdup(name);
+ return n;
+}
+
+Node *mkuse(int line, char *use, int islocal)
+{
+ Node *n;
+
+ n = mknode(line, Nuse);
+ n->use.name = strdup(use);
+ n->use.islocal = islocal;
+
+ return n;
+}
+
+Node *mksliceexpr(int line, Node *sl, Node *base, Node *off)
+{
+ if (!base)
+ base = mkintlit(line, 0);
+ if (!off)
+ off = mkexpr(line, Omemb, sl, mkname(line, "len"), NULL);
+ return mkexpr(line, Oslice, sl, base, off, NULL);
+}
+
+Node *mkexprl(int line, Op op, Node **args, size_t nargs)
+{
+ Node *n;
+
+ n = mknode(line, Nexpr);
+ n->expr.op = op;
+ n->expr.args = args;
+ n->expr.nargs = nargs;
+ return n;
+}
+
+Node *mkexpr(int line, Op op, ...)
+{
+ Node *n;
+ va_list ap;
+ Node *arg;
+
+ n = mknode(line, Nexpr);
+ n->expr.op = op;
+ va_start(ap, op);
+ while ((arg = va_arg(ap, Node*)) != NULL)
+ lappend(&n->expr.args, &n->expr.nargs, arg);
+ va_end(ap);
+
+ return n;
+}
+
+Node *mkcall(int line, Node *fn, Node **args, size_t nargs)
+{
+ Node *n;
+ size_t i;
+
+ n = mkexpr(line, Ocall, fn, NULL);
+ for (i = 0; i < nargs; i++)
+ lappend(&n->expr.args, &n->expr.nargs, args[i]);
+ return n;
+}
+
+Node *mkifstmt(int line, Node *cond, Node *iftrue, Node *iffalse)
+{
+ Node *n;
+
+ n = mknode(line, Nifstmt);
+ n->ifstmt.cond = cond;
+ n->ifstmt.iftrue = iftrue;
+ n->ifstmt.iffalse = iffalse;
+
+ return n;
+}
+
+Node *mkloopstmt(int line, Node *init, Node *cond, Node *incr, Node *body)
+{
+ Node *n;
+
+ n = mknode(line, Nloopstmt);
+ n->loopstmt.init = init;
+ n->loopstmt.cond = cond;
+ n->loopstmt.step = incr;
+ n->loopstmt.body = body;
+
+ return n;
+}
+
+Node *mkiterstmt(int line, Node *elt, Node *seq, Node *body)
+{
+ Node *n;
+
+ n = mknode(line, Niterstmt);
+ n->iterstmt.elt = elt;
+ n->iterstmt.seq = seq;
+ n->iterstmt.body = body;
+
+ return n;
+}
+
+Node *mkmatchstmt(int line, Node *val, Node **matches, size_t nmatches)
+{
+ Node *n;
+
+ n = mknode(line, Nmatchstmt);
+ n->matchstmt.val = val;
+ n->matchstmt.matches = matches;
+ n->matchstmt.nmatches = nmatches;
+ return n;
+}
+
+Node *mkmatch(int line, Node *pat, Node *body)
+{
+ Node *n;
+
+ n = mknode(line, Nmatch);
+ n->match.pat = pat;
+ n->match.block = body;
+ return n;
+}
+
+Node *mkfunc(int line, Node **args, size_t nargs, Type *ret, Node *body)
+{
+ Node *n;
+ Node *f;
+ size_t i;
+
+ f = mknode(line, Nfunc);
+ f->func.args = args;
+ f->func.nargs = nargs;
+ f->func.body = body;
+ f->func.scope = mkstab();
+ f->func.type = mktyfunc(line, args, nargs, ret);
+
+ for (i = 0; i < nargs; i++)
+ putdcl(f->func.scope, args[i]);
+
+ n = mknode(line, Nlit);
+ n->lit.littype = Lfunc;
+ n->lit.fnval = f;
+ return n;
+}
+
+Node *mkblock(int line, Stab *scope)
+{
+ Node *n;
+
+ n = mknode(line, Nblock);
+ n->block.scope = scope;
+ return n;
+}
+
+Node *mkimplstmt(int line, Node *name, Type *t, Node **decls, size_t ndecls)
+{
+ Node *n;
+
+ n = mknode(line, Nimpl);
+ n->impl.traitname = name;
+ n->impl.type = t;
+ n->impl.decls = decls;
+ n->impl.ndecls = ndecls;
+ return n;
+}
+
+
+Node *mkintlit(int line, uvlong val)
+{
+ return mkexpr(line, Olit, mkint(line, val), NULL);
+}
+
+Node *mklbl(int line, char *lbl)
+{
+ Node *n;
+
+ assert(lbl != NULL);
+ n = mknode(line, Nlit);
+ n->lit.littype = Llbl;
+ n->lit.lblval = strdup(lbl);
+ return mkexpr(line, Olit, n, NULL);
+}
+
+Node *mkstr(int line, char *val)
+{
+ Node *n;
+
+ n = mknode(line, Nlit);
+ n->lit.littype = Lstr;
+ n->lit.strval = strdup(val);
+
+ return n;
+}
+
+Node *mkint(int line, uint64_t val)
+{
+ Node *n;
+
+ n = mknode(line, Nlit);
+ n->lit.littype = Lint;
+ n->lit.intval = val;
+
+ return n;
+}
+
+Node *mkchar(int line, uint32_t val)
+{
+ Node *n;
+
+ n = mknode(line, Nlit);
+ n->lit.littype = Lchr;
+ n->lit.chrval = val;
+
+ return n;
+}
+
+Node *mkfloat(int line, double val)
+{
+ Node *n;
+
+ n = mknode(line, Nlit);
+ n->lit.littype = Lflt;
+ n->lit.fltval = val;
+
+ return n;
+}
+
+Node *mkidxinit(int line, Node *idx, Node *init)
+{
+ init->expr.idx = idx;
+ return init;
+}
+
+Node *mkname(int line, char *name)
+{
+ Node *n;
+
+ n = mknode(line, Nname);
+ n->name.name = strdup(name);
+
+ return n;
+}
+
+Node *mknsname(int line, char *ns, char *name)
+{
+ Node *n;
+
+ n = mknode(line, Nname);
+ n->name.ns = strdup(ns);
+ n->name.name = strdup(name);
+
+ return n;
+}
+
+Node *mkdecl(int line, Node *name, Type *ty)
+{
+ Node *n;
+
+ n = mknode(line, Ndecl);
+ n->decl.did = ndecls;
+ n->decl.name = name;
+ n->decl.type = ty;
+ lappend(&decls, &ndecls, n);
+ return n;
+}
+
+Ucon *mkucon(int line, Node *name, Type *ut, Type *et)
+{
+ Ucon *uc;
+
+ uc = zalloc(sizeof(Ucon));
+ uc->line = line;
+ uc->name = name;
+ uc->utype = ut;
+ uc->etype = et;
+ return uc;
+}
+
+Node *mkbool(int line, int val)
+{
+ Node *n;
+
+ n = mknode(line, Nlit);
+ n->lit.littype = Lbool;
+ n->lit.boolval = val;
+
+ return n;
+}
+
+char *declname(Node *n)
+{
+ Node *name;
+ assert(n->type == Ndecl);
+ name = n->decl.name;
+ return name->name.name;
+}
+
+Type *decltype(Node *n)
+{
+ assert(n->type == Ndecl);
+ return nodetype(n);
+}
+
+Type *exprtype(Node *n)
+{
+ assert(n->type == Nexpr);
+ return nodetype(n);
+}
+
+Type *nodetype(Node *n)
+{
+ switch (n->type) {
+ case Ndecl: return n->decl.type; break;
+ case Nexpr: return n->expr.type; break;
+ case Nlit: return n->lit.type; break;
+ default: die("Node %s has no type", nodestr(n->type)); break;
+ }
+ return NULL;
+}
+
+/* name hashing */
+ulong namehash(void *p)
+{
+ Node *n;
+
+ n = p;
+ return strhash(namestr(n)) ^ strhash(n->name.ns);
+}
+
+int nameeq(void *p1, void *p2)
+{
+ Node *a, *b;
+ a = p1;
+ b = p2;
+ if (a == b)
+ return 1;
+
+ return streq(namestr(a), namestr(b)) && streq(a->name.ns, b->name.ns);
+}
+
+void setns(Node *n, char *ns)
+{
+ n->name.ns = strdup(ns);
+}
+
+Op exprop(Node *e)
+{
+ assert(e->type == Nexpr);
+ return e->expr.op;
+}
+
+char *namestr(Node *name)
+{
+ if (!name)
+ return "";
+ assert(name->type == Nname);
+ return name->name.name;
+}
+
+static size_t did(Node *n)
+{
+ if (n->type == Ndecl) {
+ return n->decl.did;
+ } else if (n->type == Nexpr) {
+ assert(exprop(n) == Ovar);
+ return n->expr.did;
+ }
+ dump(n, stderr);
+ die("Can't get did");
+ return 0;
+}
+
+/* Hashes a Ovar expr or an Ndecl */
+ulong varhash(void *dcl)
+{
+ /* large-prime hash. meh. */
+ return did(dcl) * 366787;
+}
+
+/* Checks if the did of two vars are equal */
+int vareq(void *a, void *b)
+{
+ return did(a) == did(b);
+}
--- /dev/null
+++ b/parse/nodes.def
@@ -1,0 +1,15 @@
+N(Nnone)
+N(Nfile)
+N(Nblock)
+N(Nifstmt)
+N(Nloopstmt)
+N(Niterstmt)
+N(Nmatchstmt)
+N(Nmatch)
+N(Nuse)
+N(Nexpr)
+N(Nlit)
+N(Nname)
+N(Ndecl)
+N(Nfunc)
+N(Nimpl)
--- /dev/null
+++ b/parse/ops.def
@@ -1,0 +1,90 @@
+/* operator name, is it pure */
+O(Obad, 1)
+O(Oadd, 1)
+O(Osub, 1)
+O(Omul, 1)
+O(Odiv, 1)
+O(Omod, 1)
+O(Oneg, 1)
+O(Obor, 1)
+O(Oband, 1)
+O(Obxor, 1)
+O(Obsl, 1)
+O(Obsr, 1)
+O(Obnot, 1)
+O(Opreinc, 1)
+O(Opostinc, 1)
+O(Opredec, 1)
+O(Opostdec, 1)
+O(Oaddr, 1)
+O(Oderef, 1)
+O(Olor, 1)
+O(Oland, 1)
+O(Olnot, 1)
+O(Oeq, 1)
+O(One, 1)
+O(Ogt, 1)
+O(Oge, 1)
+O(Olt, 1)
+O(Ole, 1)
+O(Oasn, 1)
+O(Oaddeq, 1)
+O(Osubeq, 1)
+O(Omuleq, 1)
+O(Odiveq, 1)
+O(Omodeq, 1)
+O(Oboreq, 1)
+O(Obandeq, 1)
+O(Obxoreq, 1)
+O(Obsleq, 1)
+O(Obsreq, 1)
+O(Oidx, 1)
+O(Oslice, 1)
+O(Omemb, 1)
+O(Osize, 1)
+O(Ocall, 0)
+O(Ocast, 1)
+O(Oret, 1)
+O(Ojmp, 0)
+O(Obreak, 0)
+O(Ocontinue, 0)
+O(Ovar, 1)
+O(Olit, 1)
+O(Olbl, 1)
+O(Oucon, 1)
+O(Ouget, 1)
+O(Otup, 1)
+O(Ostruct, 1)
+O(Oarr, 1)
+
+/* all below this point are backend-only */
+O(Ocjmp, 1) /* conditional jump */
+O(Oset, 1) /* store to var */
+O(Osllen, 1) /* size of slice */
+O(Oslbase, 1) /* base of sice */
+O(Oblit, 1) /* block copy of memory */
+O(Otrunc, 1) /* truncating cast */
+O(Ozwiden, 1) /* zero-extending widening cast */
+O(Oswiden, 1) /* sign-extending widening cast */
+O(Oflt2int, 1) /* float to int conversion */
+O(Oint2flt, 1) /* int to float conversion */
+O(Ofadd, 1)
+O(Ofsub, 1)
+O(Ofmul, 1)
+O(Ofdiv, 1)
+O(Ofneg, 1)
+
+/* floating point comparisons */
+O(Ofeq, 1)
+O(Ofne, 1)
+O(Ofgt, 1)
+O(Ofge, 1)
+O(Oflt, 1)
+O(Ofle, 1)
+/* unsigned comparisons */
+O(Oueq, 1)
+O(Oune, 1)
+O(Ougt, 1)
+O(Ouge, 1)
+O(Oult, 1)
+O(Oule, 1)
--- /dev/null
+++ b/parse/parse.h
@@ -1,0 +1,556 @@
+#define FATAL __attribute__((noreturn))
+
+typedef uint8_t byte;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+typedef long long vlong;
+typedef unsigned long long uvlong;
+
+typedef struct Bitset Bitset;
+typedef struct Htab Htab;
+
+typedef struct Tok Tok;
+typedef struct Node Node;
+typedef struct Ucon Ucon;
+typedef struct Stab Stab;
+
+typedef struct Type Type;
+typedef struct Trait Trait;
+
+typedef enum {
+#define O(op, pure) op,
+#include "ops.def"
+ Numops,
+#undef O
+} Op;
+
+typedef enum {
+#define N(nt) nt,
+#include "nodes.def"
+#undef N
+} Ntype;
+
+typedef enum {
+#define L(lt) lt,
+#include "lits.def"
+#undef L
+} Littype;
+
+typedef enum {
+#define Ty(t, n) t,
+#include "types.def"
+#undef Ty
+ Ntypes
+} Ty;
+
+typedef enum {
+#define Tc(c, n) c,
+#include "trait.def"
+#undef Tc
+ Ntraits
+} Tc;
+
+typedef enum {
+ Visintern,
+ Visexport,
+ Vishidden,
+ Visbuiltin,
+} Vis;
+
+typedef enum {
+ Dclconst = 1 << 0,
+ Dclextern = 1 << 1,
+} Dclflags;
+
+struct Bitset {
+ size_t nchunks;
+ size_t *chunks;
+};
+
+struct Htab {
+ size_t nelt;
+ size_t sz;
+ ulong (*hash)(void *k);
+ int (*cmp)(void *a, void *b);
+ void **keys;
+ void **vals;
+ ulong *hashes;
+ char *dead;
+};
+
+struct Tok {
+ int type;
+ int line;
+ char *str;
+
+ /* values parsed out */
+ vlong intval;
+ Ty inttype; /* for explicitly specified suffixes */
+ double fltval;
+ uint32_t chrval;
+};
+
+struct Stab {
+ Stab *super;
+ Node *name;
+
+ /* Contents of stab.
+ * types and values are in separate namespaces. */
+ Htab *dcl;
+ Htab *closure; /* the syms we close over */
+ Htab *ns; /* namespaces */
+ Htab *ty; /* types */
+ Htab *tr; /* traits */
+ Htab *uc; /* union constructors */
+ Htab *impl; /* trait implementations: really a set of implemented traits. */
+};
+
+struct Type {
+ Ty type;
+ int tid;
+ int line;
+ Vis vis;
+
+ int resolved; /* Have we resolved the subtypes? Prevents infinite recursion. */
+ int fixed; /* Have we fixed the subtypes? Prevents infinite recursion. */
+
+ Bitset *traits; /* the type constraints matched on this type */
+ Node **traitlist; /* The names of the constraints on the type. Used to fill the bitset */
+ size_t ntraitlist; /* The length of the constraint list above */
+
+ int issynth; /* Tyname: whether this is synthesized or not */
+ int ishidden; /* Tyname: whether this is hidden or not */
+ Type **param; /* Tyname: type parameters that match the type args */
+ size_t nparam; /* Tyname: count of type parameters */
+ Type **arg; /* Tyname: type arguments instantiated */
+ size_t narg; /* Tyname: count of type arguments */
+ Type **inst; /* Tyname: instances created */
+ size_t ninst; /* Tyname: count of instances created */
+
+ Type **sub; /* sub-types; shared by all composite types */
+ size_t nsub; /* For compound types */
+ size_t nmemb; /* for aggregate types (struct, union) */
+ union {
+ Node *name; /* Tyname: unresolved name. Tyalias: alias name */
+ Node *asize; /* array size */
+ char *pname; /* Typaram: name of type parameter */
+ Node **sdecls; /* Tystruct: decls in struct */
+ Ucon **udecls; /* Tyunion: decls in union */
+ };
+};
+
+struct Ucon {
+ int line; /* line declared on */
+ size_t id; /* unique id */
+ int synth; /* is it generated? */
+ Node *name; /* ucon name */
+ Type *utype; /* type of the union this is an element of */
+ Type *etype; /* type for the element */
+};
+
+struct Trait {
+ int uid; /* unique id */
+ Vis vis;
+ int isproto; /* is it a prototype (for exporting purposes) */
+ int ishidden; /* should user code be able to use this? */
+ Node *name; /* the name of the trait */
+ Type *param; /* the type parameter */
+ Node **memb; /* type must have these members */
+ size_t nmemb;
+ Node **funcs; /* and declare these funcs */
+ size_t nfuncs;
+};
+
+struct Node {
+ int line;
+ Ntype type;
+ int nid;
+ union {
+ struct {
+ char *name;
+ size_t nuses;
+ Node **uses;
+ size_t nlibdeps;
+ char **libdeps;
+ size_t nstmts;
+ Node **stmts;
+ Stab *globls;
+ Stab *exports;
+ } file;
+
+ struct {
+ Op op;
+ Type *type;
+ int isconst;
+ 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 */
+ Node **args;
+ } expr;
+
+ struct {
+ char *ns;
+ char *name;
+ } name;
+
+ struct {
+ int islocal;
+ char *name;
+ } use;
+
+ struct {
+ Littype littype;
+ Type *type;
+ size_t nelt;
+ union {
+ uvlong intval;
+ double fltval;
+ uint32_t chrval;
+ char *strval;
+ char *lblval;
+ int boolval;
+ Node *fnval;
+ Node **seqval;
+ };
+ } lit;
+
+ struct {
+ Node *init;
+ Node *cond;
+ Node *step;
+ Node *body;
+ } loopstmt;
+
+ struct {
+ Node *elt;
+ Node *seq;
+ Node *body;
+ } iterstmt;
+
+ struct {
+ Node *cond;
+ Node *iftrue;
+ Node *iffalse;
+ } ifstmt;
+
+ struct {
+ Node *val;
+ size_t nmatches;
+ Node **matches;
+ } matchstmt;
+
+ struct {
+ Node *pat;
+ Node *block;
+ } match;
+
+ struct {
+ Stab *scope;
+ size_t nstmts;
+ Node **stmts;
+ } block;
+
+ struct {
+ size_t did;
+ Node *name;
+ Type *type;
+ Node *init;
+ /*
+ If we have a link to a trait, we should only look it up
+ when specializing, but we should not create a new decl
+ node for it. That will be done when specializing the
+ impl.
+ */
+ Trait *trait;
+ char vis;
+ char isglobl;
+ char isconst;
+ char isgeneric;
+ char isextern;
+ char ishidden;
+ } decl;
+
+ struct {
+ long uid;
+ Node *name;
+ Type *elt;
+ Type *alt;
+ } uelt;
+
+ struct {
+ Stab *scope;
+ Type *type;
+ size_t nargs;
+ Node **args;
+ Node *body;
+ } func;
+
+ struct {
+ Node *name;
+ size_t traitid;
+
+ Node **funcs;
+ size_t nfuncs;
+ Node **membs;
+ size_t nmembs;
+ } trait;
+
+ struct {
+ Node *traitname;
+ Trait *trait;
+ Type *type;
+ Node **decls;
+ size_t ndecls;
+ Vis vis;
+ char isproto;
+ } impl;
+ };
+};
+
+/* globals */
+extern char *filename;
+extern Tok *curtok; /* the last token we tokenized */
+extern int line; /* the last line number we tokenized */
+extern Node *file; /* the current file we're compiling */
+extern Type **tytab; /* type -> type map used by inference. size maintained by type creation code */
+extern Type **types;
+extern size_t ntypes;
+extern Trait **traittab; /* int -> trait map */
+extern size_t ntraittab;
+extern Node **decls; /* decl id -> decl map */
+extern size_t ndecls;
+extern Node **exportimpls;
+extern size_t nexportimpls;
+extern size_t maxnid; /* the maximum node id generated so far */
+
+extern int ispureop[];
+
+/* data structures */
+Bitset *mkbs(void);
+void bsfree(Bitset *bs);
+Bitset *bsdup(Bitset *bs);
+Bitset *bsclear(Bitset *bs);
+void delbs(Bitset *bs);
+void bsput(Bitset *bs, size_t elt);
+void bsdel(Bitset *bs, size_t elt);
+void bsunion(Bitset *a, Bitset *b);
+void bsintersect(Bitset *a, Bitset *b);
+void bsdiff(Bitset *a, Bitset *b);
+int bseq(Bitset *a, Bitset *b);
+int bsissubset(Bitset *set, Bitset *sub);
+int bsiter(Bitset *bs, size_t *elt);
+size_t bsmax(Bitset *bs);
+size_t bscount(Bitset *bs);
+/* inline for speed */
+static inline int bshas(Bitset *bs, size_t elt)
+{
+ if (elt >= bs->nchunks*8*sizeof(size_t))
+ return 0;
+ return (bs->chunks[elt/(8*sizeof(size_t))] & (1ULL << (elt % (8*sizeof(size_t))))) != 0;
+}
+
+Htab *mkht(ulong (*hash)(void *key), int (*cmp)(void *k1, void *k2));
+void htfree(Htab *ht);
+int htput(Htab *ht, void *k, void *v);
+void htdel(Htab *ht, void *k);
+void *htget(Htab *ht, void *k);
+int hthas(Htab *ht, void *k);
+void **htkeys(Htab *ht, size_t *nkeys);
+/* useful key types */
+ulong strhash(void *key);
+int streq(void *a, void *b);
+ulong ptrhash(void *key);
+int ptreq(void *a, void *b);
+ulong inthash(uint64_t key);
+int inteq(uint64_t a, uint64_t b);
+ulong tyhash(void *t);
+int tyeq(void *a, void *b);
+ulong namehash(void *t);
+int nameeq(void *a, void *b);
+
+/* util functions */
+void *zalloc(size_t size);
+void *xalloc(size_t size);
+void *zrealloc(void *p, size_t oldsz, size_t size);
+void *xrealloc(void *p, size_t size);
+void die(char *msg, ...) FATAL;
+void fatal(int line, char *fmt, ...) FATAL;
+char *strdupn(char *s, size_t len);
+char *strjoin(char *u, char *v);
+void *memdup(void *mem, size_t len);
+
+/* parsing etc */
+void tokinit(char *file);
+int yylex(void);
+int yyparse(void);
+
+/* stab creation */
+Stab *mkstab(void);
+
+void putns(Stab *st, Stab *scope);
+void puttype(Stab *st, Node *n, Type *ty);
+void puttrait(Stab *st, Node *n, Trait *trait);
+void putimpl(Stab *st, Node *impl);
+void updatetype(Stab *st, Node *n, Type *t);
+void putdcl(Stab *st, Node *dcl);
+void forcedcl(Stab *st, Node *dcl);
+void putucon(Stab *st, Ucon *uc);
+
+Stab *getns(Stab *st, Node *n);
+Stab *getns_str(Stab *st, char *n);
+Node *getdcl(Stab *st, Node *n);
+Type *gettype_l(Stab *st, Node *n);
+Type *gettype(Stab *st, Node *n);
+Node *getimpl(Stab *st, Node *impl);
+Trait *gettrait(Stab *st, Node *n);
+Ucon *getucon(Stab *st, Node *n);
+
+Stab *curstab(void);
+void pushstab(Stab *st);
+void popstab(void);
+
+/* type creation */
+void tyinit(Stab *st); /* sets up built in types */
+
+Type *mktype(int line, Ty ty);
+Type *tydup(Type *t); /* shallow duplicate; all subtypes/members/... kept */
+Type *mktyvar(int line);
+Type *mktyparam(int line, char *name);
+Type *mktyname(int line, Node *name, Type **params, size_t nparams, Type *base);
+Type *mktyunres(int line, Node *name, Type **params, size_t nparams);
+Type *mktyarray(int line, Type *base, Node *sz);
+Type *mktyslice(int line, Type *base);
+Type *mktyidxhack(int line, Type *base);
+Type *mktyptr(int line, Type *base);
+Type *mktytuple(int line, Type **sub, size_t nsub);
+Type *mktyfunc(int line, Node **args, size_t nargs, Type *ret);
+Type *mktystruct(int line, Node **decls, size_t ndecls);
+Type *mktyunion(int line, Ucon **decls, size_t ndecls);
+Trait *mktrait(int line, Node *name, Type *param, Node **memb, size_t nmemb, Node **funcs, size_t nfuncs, int isproto);
+Type *mktylike(int line, Ty ty); /* constrains tyvar t like it was builtin ty */
+int istysigned(Type *t);
+int istyfloat(Type *t);
+int isgeneric(Type *t);
+int hasparams(Type *t);
+
+/* type manipulation */
+Type *tybase(Type *t);
+char *tyfmt(char *buf, size_t len, Type *t);
+char *tystr(Type *t);
+
+int hastrait(Type *t, Trait *c);
+int settrait(Type *t, Trait *c);
+int traiteq(Type *t, Trait **traits, size_t len);
+int traitfmt(char *buf, size_t len, Type *t);
+char *traitstr(Type *t);
+
+/* node creation */
+Node *mknode(int line, Ntype nt);
+Node *mkfile(char *name);
+Node *mkuse(int line, char *use, int islocal);
+Node *mksliceexpr(int line, Node *sl, Node *base, Node *off);
+Node *mkexprl(int line, Op op, Node **args, size_t nargs);
+Node *mkexpr(int line, Op op, ...); /* NULL terminated */
+Node *mkcall(int line, Node *fn, Node **args, size_t nargs);
+Node *mkifstmt(int line, Node *cond, Node *iftrue, Node *iffalse);
+Node *mkloopstmt(int line, Node *init, Node *cond, Node *incr, Node *body);
+Node *mkiterstmt(int line, Node *elt, Node *seq, Node *body);
+Node *mkmatchstmt(int line, Node *val, Node **matches, size_t nmatches);
+Node *mkmatch(int line, Node *pat, Node *body);
+Node *mkblock(int line, Stab *scope);
+Node *mkimplstmt(int line, Node *name, Type *type, Node **impls, size_t nimpls);
+Node *mkintlit(int line, uvlong val);
+Node *mkidxinit(int line, Node *idx, Node *init);
+
+Node *mkbool(int line, int val);
+Node *mkint(int line, uint64_t val);
+Node *mkchar(int line, uint32_t val);
+Node *mkstr(int line, char *s);
+Node *mkfloat(int line, double flt);
+Node *mkfunc(int line, Node **args, size_t nargs, Type *ret, Node *body);
+Node *mkname(int line, char *name);
+Node *mknsname(int line, char *ns, char *name);
+Node *mkdecl(int line, Node *name, Type *ty);
+Node *mklbl(int line, char *lbl);
+Node *mkslice(int line, Node *base, Node *off);
+Ucon *mkucon(int line, Node *name, Type *ut, Type *uet);
+
+/* node util functions */
+char *namestr(Node *name);
+char *declname(Node *n);
+Type *decltype(Node *n);
+Type *exprtype(Node *n);
+Type *nodetype(Node *n);
+void addstmt(Node *file, Node *stmt);
+void setns(Node *n, char *ns);
+void updatens(Stab *st, char *ns);
+ulong varhash(void *dcl);
+int vareq(void *a, void *b);
+Op exprop(Node *n);
+
+/* specialize generics */
+Node *specializedcl(Node *n, Type *to, Node **name);
+Type *tyspecialize(Type *t, Htab *tymap);
+Node *genericname(Node *n, Type *t);
+
+/* usefiles */
+int loaduse(FILE *f, Stab *into);
+void readuse(Node *use, Stab *into);
+void writeuse(FILE *fd, Node *file);
+void tagexports(Stab *st);
+
+/* typechecking/inference */
+void infer(Node *file);
+
+/* debug */
+void dump(Node *t, FILE *fd);
+void dumpsym(Node *s, FILE *fd);
+void dumpstab(Stab *st, FILE *fd);
+char *opstr(Op o);
+char *nodestr(Ntype nt);
+char *litstr(Littype lt);
+char *tidstr(Ty tid);
+
+/* convenience funcs */
+void lappend(void *l, size_t *len, void *n); /* hack; nl is void* b/c void*** is incompatible with T*** */
+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);
+void lfree(void *l, size_t *len);
+
+/* serializing/unserializing */
+void be64(vlong v, byte buf[8]);
+vlong host64(byte buf[8]);
+void be32(long v, byte buf[4]);
+long host32(byte buf[4]);
+static inline intptr_t ptoi(void *p) {return (intptr_t)p;}
+static inline void* itop(intptr_t i) {return (void*)i;}
+
+void wrbuf(FILE *fd, void *buf, size_t sz);
+void rdbuf(FILE *fd, void *buf, size_t sz);
+char rdbyte(FILE *fd);
+void wrbyte(FILE *fd, char val);
+char rdbyte(FILE *fd);
+void wrint(FILE *fd, long val);
+long rdint(FILE *fd);
+void wrstr(FILE *fd, char *val);
+char *rdstr(FILE *fd);
+void wrflt(FILE *fd, double val);
+double rdflt(FILE *fd);
+void wrbool(FILE *fd, int val);
+int rdbool(FILE *fd);
+
+size_t max(size_t a, size_t b);
+size_t min(size_t a, size_t b);
+size_t align(size_t sz, size_t a);
+
+/* suffix replacement */
+char *swapsuffix(char *buf, size_t sz, char *s, char *suf, char *swap);
+
+/* Options to control the compilation */
+extern int yydebug;
+extern char debugopt[128];
+extern int asmonly;
+extern char *outfile;
+extern char **incpaths;
+extern size_t nincpaths;
--- /dev/null
+++ b/parse/specialize.c
@@ -1,0 +1,423 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+
+static Node *specializenode(Node *n, Htab *tsmap);
+
+void addtraits(Type *t, Bitset *traits)
+{
+ size_t b;
+
+ if (traits)
+ for (b = 0; bsiter(traits, &b); b++)
+ settrait(t, traittab[b]);
+}
+
+/*
+ * Duplicates the type 't', with all bound type
+ * parameters substituted with the substitions
+ * described in 'tsmap'
+ *
+ * Returns a fresh type with all unbound type
+ * parameters (type schemes in most literature)
+ * replaced with type variables that we can unify
+ * against */
+Type *tyspecialize(Type *t, Htab *tsmap)
+{
+ Type *ret, *tmp;
+ size_t i;
+ Type **subst;
+
+ if (hthas(tsmap, t))
+ return htget(tsmap, t);
+ switch (t->type) {
+ case Typaram:
+ ret = mktyvar(t->line);
+ addtraits(ret, t->traits);
+ htput(tsmap, t, ret);
+ break;
+ case Tyname:
+ if (!hasparams(t)) {
+ ret = t;
+ } else {
+ if (t->narg)
+ subst = t->arg;
+ else
+ subst = t->param;
+ for (i = 0; i < t->nparam; i++) {
+ if (subst[i]->type != Typaram || hthas(tsmap, subst[i]))
+ continue;
+ tmp = mktyvar(subst[i]->line);
+ addtraits(tmp, subst[i]->traits);
+ htput(tsmap, subst[i], tmp);
+ }
+ ret = mktyname(t->line, t->name, t->param, t->nparam, tyspecialize(t->sub[0], tsmap));
+ ret->issynth = 1;
+ htput(tsmap, t, ret);
+ for (i = 0; i < t->nparam; i++)
+ lappend(&ret->arg, &ret->narg, tyspecialize(subst[i], tsmap));
+ }
+ break;
+ case Tystruct:
+ ret = tydup(t);
+ htput(tsmap, t, ret);
+ pushstab(NULL);
+ for (i = 0; i < t->nmemb; i++)
+ ret->sdecls[i] = specializenode(t->sdecls[i], tsmap);
+ popstab();
+ break;
+ case Tyunion:
+ ret = tydup(t);
+ htput(tsmap, t, ret);
+ for (i = 0; i < t->nmemb; i++) {
+ tmp = NULL;
+ if (ret->udecls[i]->etype)
+ tmp = tyspecialize(t->udecls[i]->etype, tsmap);
+ ret->udecls[i] = mkucon(t->line, t->udecls[i]->name, ret, tmp);
+ ret->udecls[i]->utype = ret;
+ ret->udecls[i]->id = i;
+ ret->udecls[i]->synth = 1;
+ }
+ break;
+ default:
+ if (t->nsub > 0) {
+ ret = tydup(t);
+ htput(tsmap, t, ret);
+ for (i = 0; i < t->nsub; i++)
+ ret->sub[i] = tyspecialize(t->sub[i], tsmap);
+ } else {
+ ret = t;
+ }
+ break;
+ }
+ return ret;
+}
+
+/* Checks if the type 't' is generic, and if it is
+ * substitutes the types. This is here for efficiency,
+ * so we don't gratuitously duplicate types */
+static Type *tysubst(Type *t, Htab *tsmap)
+{
+ if (hasparams(t))
+ return tyspecialize(t, tsmap);
+ else
+ return t;
+}
+
+/*
+ * Fills the substitution map with a mapping from
+ * the type parameter 'from' to it's substititon 'to'
+ */
+static void fillsubst(Htab *tsmap, Type *to, Type *from)
+{
+ size_t i;
+
+ if (from->type == Typaram) {
+ if (debugopt['S'])
+ printf("mapping %s => %s\n", tystr(from), tystr(to));
+ htput(tsmap, from, to);
+ return;
+ }
+ assert(to->nsub == from->nsub);
+ for (i = 0; i < to->nsub; i++)
+ fillsubst(tsmap, to->sub[i], from->sub[i]);
+ if (to->type == Tyname && to->nparam > 0) {
+ assert(to->nparam == to->narg);
+ for (i = 0; i < to->nparam; i++)
+ fillsubst(tsmap, to->arg[i], to->param[i]);
+ }
+}
+
+/*
+ * Fixes up nodes. This involves fixing up the
+ * declaration identifiers once we specialize
+ */
+static void fixup(Node *n)
+{
+ size_t i;
+ Node *d;
+ Stab *ns;
+
+ if (!n)
+ return;
+ switch (n->type) {
+ case Nfile:
+ case Nuse:
+ die("Node %s not allowed here\n", nodestr(n->type));
+ break;
+ case Nexpr:
+ fixup(n->expr.idx);
+ for (i = 0; i < n->expr.nargs; i++)
+ fixup(n->expr.args[i]);
+ if (n->expr.op == Ovar) {
+ ns = curstab();
+ if (n->expr.args[0]->name.ns)
+ ns = getns_str(ns, n->expr.args[0]->name.ns);
+ if (!ns)
+ fatal(n->line, "No namespace %s\n", n->expr.args[0]->name.ns);
+ d = getdcl(ns, n->expr.args[0]);
+ if (!d)
+ die("Missing decl %s", namestr(n->expr.args[0]));
+ if (d->decl.isgeneric)
+ d = specializedcl(d, n->expr.type, &n->expr.args[0]);
+ n->expr.did = d->decl.did;
+ }
+ break;
+ case Nlit:
+ switch (n->lit.littype) {
+ case Lfunc: fixup(n->lit.fnval); break;
+ case Lchr: case Lint: case Lflt:
+ case Lstr: case Llbl: case Lbool:
+ break;
+ }
+ break;
+ case Nifstmt:
+ fixup(n->ifstmt.cond);
+ fixup(n->ifstmt.iftrue);
+ fixup(n->ifstmt.iffalse);
+ break;
+ case Nloopstmt:
+ fixup(n->loopstmt.init);
+ fixup(n->loopstmt.cond);
+ fixup(n->loopstmt.step);
+ fixup(n->loopstmt.body);
+ break;
+ case Niterstmt:
+ fixup(n->iterstmt.elt);
+ fixup(n->iterstmt.seq);
+ fixup(n->iterstmt.body);
+ break;
+ case Nmatchstmt:
+ fixup(n->matchstmt.val);
+ for (i = 0; i < n->matchstmt.nmatches; i++)
+ fixup(n->matchstmt.matches[i]);
+ break;
+ case Nmatch:
+ /* patterns are evaluated in their block's scope */
+ pushstab(n->match.block->block.scope);
+ fixup(n->match.pat);
+ popstab();
+ fixup(n->match.block);
+ break;
+ case Nblock:
+ pushstab(n->block.scope);
+ for (i = 0; i < n->block.nstmts; i++)
+ fixup(n->block.stmts[i]);
+ popstab();
+ break;
+ case Ndecl:
+ fixup(n->decl.init);
+ break;
+ case Nfunc:
+ pushstab(n->func.scope);
+ fixup(n->func.body);
+ popstab();
+ break;
+ case Nnone: case Nname:
+ break;
+ case Nimpl:
+ die("trait/impl not implemented");
+ break;
+ }
+}
+
+
+/*
+ * Duplicates a node, replacing all things that
+ * need to be specialized to make it concrete
+ * instead of generic, and returns it.
+ */
+static Node *specializenode(Node *n, Htab *tsmap)
+{
+ Node *r;
+ size_t i;
+
+ if (!n)
+ return NULL;
+ r = mknode(n->line, n->type);
+ switch (n->type) {
+ case Nfile:
+ case Nuse:
+ die("Node %s not allowed here\n", nodestr(n->type));
+ break;
+ case Nexpr:
+ r->expr.op = n->expr.op;
+ r->expr.type = tysubst(n->expr.type, tsmap);
+ r->expr.isconst = n->expr.isconst;
+ r->expr.nargs = n->expr.nargs;
+ r->expr.idx = specializenode(n->expr.idx, tsmap);
+ r->expr.args = xalloc(n->expr.nargs * sizeof(Node*));
+ for (i = 0; i < n->expr.nargs; i++)
+ r->expr.args[i] = specializenode(n->expr.args[i], tsmap);
+ break;
+ case Nname:
+ if (n->name.ns)
+ r->name.ns = strdup(n->name.ns);
+ r->name.name = strdup(n->name.name);
+ break;
+ case Nlit:
+ r->lit.littype = n->lit.littype;
+ r->lit.type = tysubst(n->expr.type, tsmap);
+ switch (n->lit.littype) {
+ case Lchr: r->lit.chrval = n->lit.chrval; break;
+ case Lint: r->lit.intval = n->lit.intval; break;
+ case Lflt: r->lit.fltval = n->lit.fltval; break;
+ case Lstr: r->lit.strval = n->lit.strval; break;
+ case Llbl: r->lit.lblval = n->lit.lblval; break;
+ case Lbool: r->lit.boolval = n->lit.boolval; break;
+ case Lfunc: r->lit.fnval = specializenode(n->lit.fnval, tsmap); break;
+ }
+ break;
+ case Nifstmt:
+ r->ifstmt.cond = specializenode(n->ifstmt.cond, tsmap);
+ r->ifstmt.iftrue = specializenode(n->ifstmt.iftrue, tsmap);
+ r->ifstmt.iffalse = specializenode(n->ifstmt.iffalse, tsmap);
+ break;
+ case Nloopstmt:
+ r->loopstmt.init = specializenode(n->loopstmt.init, tsmap);
+ r->loopstmt.cond = specializenode(n->loopstmt.cond, tsmap);
+ r->loopstmt.step = specializenode(n->loopstmt.step, tsmap);
+ r->loopstmt.body = specializenode(n->loopstmt.body, tsmap);
+ break;
+ case Niterstmt:
+ r->iterstmt.elt = specializenode(n->iterstmt.elt, tsmap);
+ r->iterstmt.seq = specializenode(n->iterstmt.seq, tsmap);
+ r->iterstmt.body = specializenode(n->iterstmt.body, tsmap);
+ break;
+ case Nmatchstmt:
+ r->matchstmt.val = specializenode(n->matchstmt.val, tsmap);
+ r->matchstmt.nmatches = n->matchstmt.nmatches;
+ r->matchstmt.matches = xalloc(n->matchstmt.nmatches * sizeof(Node*));
+ for (i = 0; i < n->matchstmt.nmatches; i++)
+ r->matchstmt.matches[i] = specializenode(n->matchstmt.matches[i], tsmap);
+ break;
+ case Nmatch:
+ r->match.pat = specializenode(n->match.pat, tsmap);
+ r->match.block = specializenode(n->match.block, tsmap);
+ break;
+ case Nblock:
+ r->block.scope = mkstab();
+ r->block.scope->super = curstab();
+ pushstab(r->block.scope);
+ r->block.nstmts = n->block.nstmts;
+ r->block.stmts = xalloc(sizeof(Node *)*n->block.nstmts);
+ for (i = 0; i < n->block.nstmts; i++)
+ r->block.stmts[i] = specializenode(n->block.stmts[i], tsmap);
+ popstab();
+ break;
+ case Ndecl:
+ r->decl.did = ndecls;
+ /* sym */
+ r->decl.name = specializenode(n->decl.name, tsmap);
+ r->decl.type = tysubst(n->decl.type, tsmap);
+
+ /* symflags */
+ r->decl.isconst = n->decl.isconst;
+ r->decl.isgeneric = n->decl.isgeneric;
+ r->decl.isextern = n->decl.isextern;
+ if (curstab())
+ putdcl(curstab(), r);
+
+ /* init */
+ r->decl.init = specializenode(n->decl.init, tsmap);
+ lappend(&decls, &ndecls, r);
+ break;
+ case Nfunc:
+ r->func.scope = mkstab();
+ r->func.scope->super = curstab();
+ pushstab(r->func.scope);
+ r->func.type = tysubst(n->func.type, tsmap);
+ r->func.nargs = n->func.nargs;
+ r->func.args = xalloc(sizeof(Node *)*n->func.nargs);
+ for (i = 0; i < n->func.nargs; i++)
+ r->func.args[i] = specializenode(n->func.args[i], tsmap);
+ r->func.body = specializenode(n->func.body, tsmap);
+ popstab();
+ break;
+ case Nimpl:
+ die("trait/impl not implemented");
+ case Nnone:
+ die("Nnone should not be seen as node type!");
+ break;
+ }
+ return r;
+}
+Node *genericname(Node *n, Type *t)
+{
+ char buf[1024];
+ char *p, *s;
+ char *end;
+ Node *name;
+
+ if (!n->decl.isgeneric)
+ return n->decl.name;
+ p = buf;
+ end = buf + 1024;
+ s = tystr(t);
+ p += snprintf(p, end - p, "%s", n->decl.name->name.name);
+ p += snprintf(p, end - p, "$%lu", strhash(s));
+ free(s);
+ name = mkname(n->line, buf);
+ if (n->decl.name->name.ns)
+ setns(name, n->decl.name->name.ns);
+ return name;
+}
+
+/*
+ * Takes a generic declaration, and creates a specialized
+ * duplicate of it with type 'to'. It also generates
+ * a name for this specialized node, and returns it in '*name'.
+ */
+Node *specializedcl(Node *n, Type *to, Node **name)
+{
+ Htab *tsmap;
+ Node *d;
+ Node *ns;
+ Stab *st;
+ extern int stabstkoff;
+
+ assert(n->type == Ndecl);
+ assert(n->decl.isgeneric);
+
+ *name = genericname(n, to);
+ d = getdcl(file->file.globls, *name);
+ if (debugopt['S'])
+ printf("depth[%d] specializing [%d]%s => %s\n", stabstkoff, n->line, namestr(n->decl.name), namestr(*name));
+ if (d)
+ return d;
+ if (n->decl.trait)
+ fatal(n->line, "No trait implemented for for %s\n", namestr(n->decl.name));
+ /* namespaced names need to be looked up in their correct
+ * context. */
+ if (n->decl.name->name.ns) {
+ ns = mkname(n->line, n->decl.name->name.ns);
+ st = getns(file->file.globls, ns);
+ pushstab(st);
+ }
+
+ /* specialize */
+ tsmap = mkht(tyhash, tyeq);
+ fillsubst(tsmap, to, n->decl.type);
+
+ d = mkdecl(n->line, *name, tysubst(n->decl.type, tsmap));
+ d->decl.isconst = n->decl.isconst;
+ d->decl.isextern = n->decl.isextern;
+ d->decl.init = specializenode(n->decl.init, tsmap);
+ putdcl(file->file.globls, d);
+
+ fixup(d);
+
+ lappend(&file->file.stmts, &file->file.nstmts, d);
+ if (d->decl.name->name.ns)
+ popstab();
+ return d;
+}
--- /dev/null
+++ b/parse/stab.c
@@ -1,0 +1,324 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+
+/* Allows us to look up types/traits by name nodes */
+typedef struct Tydefn Tydefn;
+typedef struct Traitdefn Traitdefn;
+struct Tydefn {
+ int line;
+ Node *name;
+ Type *type;
+};
+
+struct Traitdefn {
+ int line;
+ Node *name;
+ Trait *trait;
+};
+
+#define Maxstabdepth 128
+static Stab *stabstk[Maxstabdepth];
+int stabstkoff;
+
+/* scope management */
+Stab *curstab()
+{
+ assert(stabstkoff > 0);
+ return stabstk[stabstkoff - 1];
+}
+
+void pushstab(Stab *st)
+{
+ assert(stabstkoff < Maxstabdepth);
+ stabstk[stabstkoff++] = st;
+}
+
+void popstab(void)
+{
+ assert(stabstkoff > 0);
+ stabstkoff--;
+}
+
+/* name hashing: we want namespaced lookups to find the
+ * name even if we haven't set the namespace up, since
+ * we can update it after the fact. */
+static ulong nsnamehash(void *n)
+{
+ return strhash(namestr(n));
+}
+
+static int nsnameeq(void *a, void *b)
+{
+ return a == b || !strcmp(namestr(a), namestr(b));
+}
+
+static ulong implhash(void *p)
+{
+ Node *n;
+ ulong h;
+
+ n = p;
+ h = nsnamehash(n->impl.traitname);
+ h *= tyhash(n->impl.type);
+ return h;
+}
+
+static int impleq(void *pa, void *pb)
+{
+ Node *a, *b;
+
+ a = pa;
+ b = pb;
+ if (nsnameeq(a->impl.traitname, b->impl.traitname))
+ return tyeq(a->impl.type, b->impl.type);
+ return 0;
+}
+
+Stab *mkstab()
+{
+ Stab *st;
+
+ st = zalloc(sizeof(Stab));
+ st->ns = mkht(strhash, streq);
+ st->dcl = mkht(nsnamehash, nsnameeq);
+ st->ty = mkht(nsnamehash, nsnameeq);
+ st->tr = mkht(nsnamehash, nsnameeq);
+ st->uc = mkht(nsnamehash, nsnameeq);
+ st->impl = mkht(implhash, impleq);
+ return st;
+}
+
+/*
+ * Searches for declarations from current
+ * scope, and all enclosing scopes. Does
+ * not resolve namespaces -- that is the job
+ * of the caller of this function.
+ *
+ * If a resoved name is not global, and is
+ * not in the current scope, it is recorded
+ * in the scope's closure.
+ */
+Node *getdcl(Stab *st, Node *n)
+{
+ Node *s;
+ Stab *orig;
+
+ orig = st;
+ do {
+ s = htget(st->dcl, n);
+ if (s) {
+ /* record that this is in the closure of this scope */
+ if (!st->closure)
+ st->closure = mkht(nsnamehash, nsnameeq);
+ if (st != orig && !n->decl.isglobl)
+ htput(st->closure, s->decl.name, s);
+ return s;
+ }
+ st = st->super;
+ } while (st);
+ return NULL;
+}
+
+Type *gettype_l(Stab *st, Node *n)
+{
+ Tydefn *t;
+
+ if ((t = htget(st->ty, n)))
+ return t->type;
+ return NULL;
+}
+
+
+Type *gettype(Stab *st, Node *n)
+{
+ Tydefn *t;
+
+ do {
+ if ((t = htget(st->ty, n)))
+ return t->type;
+ st = st->super;
+ } while (st);
+ return NULL;
+}
+
+Ucon *getucon(Stab *st, Node *n)
+{
+ Ucon *uc;
+
+ do {
+ if ((uc = htget(st->uc, n)))
+ return uc;
+ st = st->super;
+ } while (st);
+ return NULL;
+}
+
+Trait *gettrait(Stab *st, Node *n)
+{
+ Traitdefn *c;
+
+ do {
+ if ((c = htget(st->tr, n)))
+ return c->trait;
+ st = st->super;
+ } while (st);
+ return NULL;
+}
+
+Stab *getns_str(Stab *st, char *name)
+{
+ Stab *s;
+
+ if (!strcmp(namestr(st->name), name))
+ return st;
+ do {
+ if ((s = htget(st->ns, name)))
+ return s;
+ st = st->super;
+ } while (st);
+ return NULL;
+}
+
+Stab *getns(Stab *st, Node *n)
+{
+ return getns_str(st, namestr(n));
+}
+
+void putdcl(Stab *st, Node *s)
+{
+ Node *d;
+
+ d = htget(st->dcl, s->decl.name);
+ if (d)
+ fatal(s->line, "%s already declared (on line %d)", namestr(s->decl.name), d->line);
+ forcedcl(st, s);
+}
+
+void forcedcl (Stab *st, Node *s) {
+ if (st->name)
+ setns(s->decl.name, namestr(st->name));
+ htput(st->dcl, s->decl.name, s);
+ assert(htget(st->dcl, s->decl.name) != NULL);
+}
+
+void updatetype(Stab *st, Node *n, Type *t)
+{
+ Tydefn *td;
+
+ td = htget(st->ty, n);
+ if (!td)
+ die("No type %s to update", namestr(n));
+ td->type = t;
+}
+
+void puttype(Stab *st, Node *n, Type *t)
+{
+ Tydefn *td;
+
+ if (gettype(st, n))
+ fatal(n->line, "Type %s already defined", tystr(gettype(st, n)));
+ td = xalloc(sizeof(Tydefn));
+ td->line = n->line;
+ td->name = n;
+ td->type = t;
+ if (st->name)
+ setns(n, namestr(st->name));
+ htput(st->ty, td->name, td);
+}
+
+void putucon(Stab *st, Ucon *uc)
+{
+ if (getucon(st, uc->name))
+ fatal(uc->line, "union constructor %s already defined", namestr(uc->name));
+ htput(st->uc, uc->name, uc);
+}
+
+void puttrait(Stab *st, Node *n, Trait *c)
+{
+ Traitdefn *td;
+
+ if (gettrait(st, n))
+ fatal(n->line, "Trait %s already defined", namestr(n));
+ if (gettype(st, n))
+ fatal(n->line, "Trait %s already defined as a type", namestr(n));
+ td = xalloc(sizeof(Tydefn));
+ td->line = n->line;
+ td->name = n;
+ td->trait = c;
+ htput(st->tr, td->name, td);
+}
+
+void putimpl(Stab *st, Node *n)
+{
+ if (getimpl(st, n))
+ fatal(n->line, "Trait %s already implemented over %s", namestr(n->impl.traitname), tystr(n->impl.type));
+ if (st->name)
+ setns(n->impl.traitname, namestr(st->name));
+ htput(st->impl, n, n);
+}
+
+Node *getimpl(Stab *st, Node *n)
+{
+ Node *imp;
+
+ do {
+ if ((imp = htget(st->impl, n)))
+ return imp;
+ st = st->super;
+ } while (st);
+ return NULL;
+}
+
+void putns(Stab *st, Stab *scope)
+{
+ Stab *s;
+
+ s = getns(st, scope->name);
+ if (s)
+ fatal(scope->name->line, "Ns %s already defined", namestr(s->name));
+ htput(st->ns, namestr(scope->name), scope);
+}
+
+/*
+ * Sets the namespace of a symbol table, and
+ * changes the namespace of all contained symbols
+ * to match it.
+ */
+void updatens(Stab *st, char *name)
+{
+ void **k;
+ size_t i, nk;
+ Tydefn *td;
+
+ if (st->name)
+ die("Stab %s already has namespace; Can't set to %s", namestr(st->name), name);
+ st->name = mkname(-1, name);
+ k = htkeys(st->dcl, &nk);
+ for (i = 0; i < nk; i++)
+ setns(k[i], name);
+ free(k);
+ k = htkeys(st->ty, &nk);
+ for (i = 0; i < nk; i++)
+ setns(k[i], name);
+ for (i = 0; i < nk; i++) {
+ td = htget(st->ty, k[i]);
+ if (td->type && td->type->type == Tyname)
+ setns(td->type->name, name);
+ }
+ free(k);
+ k = htkeys(st->ns, &nk);
+ for (i = 0; i < nk; i++)
+ setns(k[i], name);
+ free(k);
+}
--- /dev/null
+++ b/parse/tok.c
@@ -1,0 +1,821 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+#include <unistd.h>
+
+#include "parse.h"
+
+#include "gram.h"
+
+#define End (-1)
+
+char *filename;
+int line;
+Tok *curtok;
+
+/* the file contents are stored globally */
+static int fidx;
+static int fbufsz;
+static char *fbuf;
+
+static int peekn(int n)
+{
+ if (fidx + n >= fbufsz)
+ return End;
+ else
+ return fbuf[fidx + n];
+}
+
+static int peek(void)
+{
+ return peekn(0);
+}
+
+static int next(void)
+{
+ int c;
+
+ c = peek();
+ fidx++;
+ return c;
+}
+
+static void unget(void)
+{
+ fidx--;
+ assert(fidx >= 0);
+}
+
+/*
+ * Consumes the character iff
+ * the character is equal to 'c'.
+ * returns true if there was a match,
+ * false otherwise.
+ */
+static int match(char c)
+{
+ if (peek() == c) {
+ next();
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static Tok *mktok(int tt)
+{
+ Tok *t;
+
+ t = zalloc(sizeof(Tok));
+ t->type = tt;
+ t->line = line;
+ return t;
+}
+
+static int identchar(int c)
+{
+ return isalnum(c) || c == '_';
+}
+
+static void eatcomment(void)
+{
+ int depth;
+ int startln;
+ int c;
+
+ depth = 0;
+ startln = line;
+ while (1) {
+ c = next();
+ switch (c) {
+ /* enter level of nesting */
+ case '/':
+ if (match('*'))
+ depth++;
+ break;
+ /* leave level of nesting */
+ case '*':
+ if (match('/'))
+ depth--;
+ break;
+ /* have to keep line numbers synced */
+ case '\n':
+ line++;
+ break;
+ case End:
+ fatal(line, "File ended within comment starting at line %d", startln);
+ break;
+ }
+ if (depth == 0)
+ break;
+ }
+}
+
+/*
+ * Consumes all forms of whitespace,
+ * including comments. If we are in a
+ * state where we should ignore newlines,
+ * we also consume '\n'. ';' is still
+ * accepted as a line ending.
+ */
+static void eatspace(void)
+{
+ int c;
+ int ignorenl;
+
+ ignorenl = 0;
+ while (1) {
+ c = peek();
+ if (!ignorenl && c == '\n') {
+ ignorenl = 0;
+ break;
+ } else if (c == '\\') {
+ ignorenl = 1;
+ next();
+ } else if (isspace(c) || (ignorenl && c == '\n')) {
+ next();
+ } else if (c == '/' && peekn(1) == '*') {
+ eatcomment();
+ } else {
+ break;
+ }
+ }
+}
+
+/*
+ * Decides if an identifier is a
+ * keyword or not. Returns the
+ * token type to use for the
+ * identifier.
+ */
+static int kwd(char *s)
+{
+ static const struct {char* kw; int tt;} kwmap[] = {
+ {"break", Tbreak},
+ {"castto", Tcast},
+ {"const", Tconst},
+ {"continue", Tcontinue},
+ {"elif", Telif},
+ {"else", Telse},
+ {"export", Texport},
+ {"extern", Textern},
+ {"false", Tboollit},
+ {"for", Tfor},
+ {"generic", Tgeneric},
+ {"goto", Tgoto},
+ {"if", Tif},
+ {"impl", Timpl},
+ {"in", Tin},
+ {"match", Tmatch},
+ {"pkg", Tpkg},
+ {"protect", Tprotect},
+ {"sizeof", Tsizeof},
+ {"struct", Tstruct},
+ {"trait", Ttrait},
+ {"true", Tboollit},
+ {"type", Ttype},
+ {"union", Tunion},
+ {"use", Tuse},
+ {"var", Tvar},
+ {"while", Twhile},
+ };
+
+ size_t min, max, mid;
+ int cmp;
+
+
+ min = 0;
+ max = sizeof(kwmap)/sizeof(kwmap[0]);
+ while (max > min) {
+ mid = (max + min) / 2;
+ cmp = strcmp(s, kwmap[mid].kw);
+ if (cmp == 0)
+ return kwmap[mid].tt;
+ else if (cmp > 0)
+ min = mid + 1;
+ else if (cmp < 0)
+ max = mid;
+ }
+ return Tident;
+}
+
+static int identstr(char *buf, size_t sz)
+{
+ size_t i;
+ char c;
+
+ i = 0;
+ for (c = peek(); i < sz && identchar(c); c = peek()) {
+ next();
+ buf[i++] = c;
+ }
+ buf[i] = '\0';
+ return i;
+}
+
+static Tok *kwident(void)
+{
+ char buf[1024];
+ Tok *t;
+
+ if (!identstr(buf, sizeof buf))
+ return NULL;
+ t = mktok(kwd(buf));
+ t->str = strdup(buf);
+ return t;
+}
+
+static void append(char **buf, size_t *len, size_t *sz, int c)
+{
+ if (!*sz) {
+ *sz = 16;
+ *buf = malloc(*sz);
+ }
+ if (*len == *sz - 1) {
+ *sz = *sz * 2;
+ *buf = realloc(*buf, *sz);
+ }
+
+ buf[0][len[0]++] = c;
+}
+
+
+static void encode(char *buf, size_t len, uint32_t c)
+{
+ int mark;
+ size_t i;
+
+ assert(len > 0 && len < 5);
+ if (len == 1)
+ mark = 0;
+ else
+ mark = (((1 << (8 - len)) - 1) ^ 0xff);
+ for (i = len - 1; i > 0; i--) {
+ buf[i] = (c & 0x3f) | 0x80;
+ c >>= 6;
+ }
+ buf[0] = (c | mark);
+}
+
+/*
+ * Appends a unicode codepoint 'c' to a growable buffer 'buf',
+ * resizing if needed.
+ */
+static void appendc(char **buf, size_t *len, size_t *sz, uint32_t c)
+{
+ size_t i, charlen;
+ char charbuf[5] = {0};
+
+ if (c < 0x80)
+ charlen = 1;
+ else if (c < 0x800)
+ charlen = 2;
+ else if (c < 0x10000)
+ charlen = 3;
+ else if (c < 0x200000)
+ charlen = 4;
+ else
+ fatal(line, "invalid utf character '\\u{%x}'", c);
+
+ encode(charbuf, charlen, c);
+ for (i = 0; i < charlen; i++)
+ append(buf, len, sz, charbuf[i]);
+}
+
+static int ishexval(char c)
+{
+ if (c >= 'a' && c <= 'f')
+ return 1;
+ else if (c >= 'A' && c <= 'F')
+ return 1;
+ else if (c >= '0' && c <= '9')
+ return 1;
+ return 0;
+}
+
+/*
+ * Converts a character to its hex value.
+ */
+static int hexval(char c)
+{
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ else if (c >= '0' && c <= '9')
+ return c - '0';
+ fatal(line, "passed non-hex value '%c' to where hex was expected", c);
+ return -1;
+}
+
+/* \u{abc} */
+static int32_t unichar()
+{
+ uint32_t v;
+ int c;
+
+ /* we've already seen the \u */
+ if (next() != '{')
+ fatal(line, "\\u escape sequence without initial '{'");
+ v = 0;
+ while (ishexval(peek())) {
+ c = next();
+ v = 16*v + hexval(c);
+ if (v > 0x10FFFF)
+ fatal(line, "invalid codepoint for \\u escape sequence");
+ }
+ if (next() != '}')
+ fatal(line, "\\u escape sequence without ending '}'");
+ return v;
+}
+
+/*
+ * decodes an escape code. These are
+ * shared between strings and characters.
+ * Unknown escape codes are ignored.
+ */
+static int decode(char **buf, size_t *len, size_t *sz)
+{
+ char c, c1, c2;
+ int32_t v;
+
+ c = next();
+ /* we've already seen the '\' */
+ switch (c) {
+ case 'u':
+ v = unichar();
+ appendc(buf, len, sz, v);
+ return v;
+ case 'x': /* arbitrary hex */
+ c1 = next();
+ if (!isxdigit(c1))
+ fatal(line, "expected hex digit, got %c", c1);
+ c2 = next();
+ if (!isxdigit(c2))
+ fatal(line, "expected hex digit, got %c", c1);
+ v = 16*hexval(c1) + hexval(c2);
+ break;
+ case 'n': v = '\n'; break;
+ case 'r': v = '\r'; break;
+ case 't': v = '\t'; break;
+ case 'b': v = '\b'; break;
+ case '"': v = '\"'; break;
+ case '\'': v = '\''; break;
+ case 'v': v = '\v'; break;
+ case '\\': v = '\\'; break;
+ case '0': v = '\0'; break;
+ default: fatal(line, "unknown escape code \\%c", c);
+ }
+ append(buf, len, sz, v);
+ return v;
+}
+
+static Tok *strlit()
+{
+ Tok *t;
+ int c;
+ size_t len, sz;
+ char *buf;
+
+ assert(next() == '"');
+
+ buf = NULL;
+ len = 0;
+ sz = 0;
+ while (1) {
+ c = next();
+ /* we don't unescape here, but on output */
+ if (c == '"')
+ break;
+ else if (c == End)
+ fatal(line, "Unexpected EOF within string");
+ else if (c == '\n')
+ fatal(line, "Newlines not allowed in strings");
+ else if (c == '\\')
+ decode(&buf, &len, &sz);
+ else
+ append(&buf, &len, &sz, c);
+ };
+ append(&buf, &len, &sz, '\0');
+
+ t = mktok(Tstrlit);
+ t->str = buf;
+ return t;
+}
+
+static uint32_t readutf(char c, char **buf, size_t *buflen, size_t *sz) {
+ size_t i, len;
+ uint32_t val;
+
+ if ((c & 0x80) == 0)
+ len = 1;
+ else if ((c & 0xe0) == 0xc0)
+ len = 2;
+ else if ((c & 0xf0) == 0xe0)
+ len = 3;
+ else if ((c & 0xf8) == 0xf0)
+ len = 4;
+ else
+ fatal(line, "Invalid utf8 encoded character constant");
+
+ val = c & ((1 << (8 - len)) - 1);
+ append(buf, buflen, sz, c);
+ for (i = 1; i < len; i++) {
+ c = next();
+ if ((c & 0xc0) != 0x80)
+ fatal(line, "Invalid utf8 codepoint in character literal");
+ val = (val << 6) | (c & 0x3f);
+ append(buf, buflen, sz, c);
+ }
+ return val;
+}
+
+static Tok *charlit()
+{
+ Tok *t;
+ int c;
+ uint32_t val;
+ size_t len, sz;
+ char *buf;
+
+
+ assert(next() == '\'');
+
+ buf = NULL;
+ len = 0;
+ sz = 0;
+ val = 0;
+ c = next();
+ if (c == End)
+ fatal(line, "Unexpected EOF within char lit");
+ else if (c == '\n')
+ fatal(line, "Newlines not allowed in char lit");
+ else if (c == '\\')
+ val = decode(&buf, &len, &sz);
+ else
+ val = readutf(c, &buf, &len, &sz);
+ append(&buf, &len, &sz, '\0');
+ if (next() != '\'')
+ fatal(line, "Character constant with multiple characters");
+
+ t = mktok(Tchrlit);
+ t->chrval = val;
+ t->str = buf;
+ return t;
+}
+
+static Tok *oper(void)
+{
+ int tt;
+ char c;
+
+ c = next();
+ switch (c) {
+ case '{': tt = Tobrace; break;
+ case '}': tt = Tcbrace; break;
+ case '(': tt = Toparen; break;
+ case ')': tt = Tcparen; break;
+ case '[': tt = Tosqbrac; break;
+ case ']': tt = Tcsqbrac; break;
+ case ',': tt = Tcomma; break;
+ case '`': tt = Ttick; break;
+ case '#': tt = Tderef; break;
+ case ':':
+ if (match(':'))
+ tt = Twith;
+ else
+ tt = Tcolon;
+ break;
+ case '~': tt = Tbnot; break;
+ case ';':
+ if (match(';'))
+ tt = Tendblk;
+ else
+ tt = Tendln;
+ break;
+ case '.':
+ if (match('.')) {
+ if (match('.')) {
+ tt = Tellipsis;
+ } else {
+ unget();
+ tt = Tdot;
+ }
+ } else {
+ tt = Tdot;
+ }
+ break;
+ case '+':
+ if (match('='))
+ tt = Taddeq;
+ else if (match('+'))
+ tt = Tinc;
+ else
+ tt = Tplus;
+ break;
+ case '-':
+ if (match('='))
+ tt = Tsubeq;
+ else if (match('-'))
+ tt = Tdec;
+ else if (match('>'))
+ tt = Tret;
+ else
+ tt = Tminus;
+ break;
+ case '*':
+ if (match('='))
+ tt = Tmuleq;
+ else
+ tt = Tmul;
+ break;
+ case '/':
+ if (match('='))
+ tt = Tdiveq;
+ else
+ tt = Tdiv;
+ break;
+ case '%':
+ if (match('='))
+ tt = Tmodeq;
+ else
+ tt = Tmod;
+ break;
+ case '=':
+ if (match('='))
+ tt = Teq;
+ else
+ tt = Tasn;
+ break;
+ case '|':
+ if (match('='))
+ tt = Tboreq;
+ else if (match('|'))
+ tt = Tlor;
+ else
+ tt = Tbor;
+ break;
+ case '&':
+ if (match('='))
+ tt = Tbandeq;
+ else if (match('&'))
+ tt = Tland;
+ else
+ tt = Tband;
+ break;
+ case '^':
+ if (match('='))
+ tt = Tbxoreq;
+ else
+ tt = Tbxor;
+ break;
+ case '<':
+ if (match('=')) {
+ tt = Tle;
+ } else if (match('<')) {
+ if (match('='))
+ tt = Tbsleq;
+ else
+ tt = Tbsl;
+ } else {
+ tt = Tlt;
+ }
+ break;
+ case '>':
+ if (match('=')) {
+ tt = Tge;
+ } else if (match('>')) {
+ if (match('='))
+ tt = Tbsreq;
+ else
+ tt = Tbsr;
+ } else {
+ tt = Tgt;
+ }
+ break;
+
+ case '!':
+ if (match('='))
+ tt = Tne;
+ else
+ tt = Tlnot;
+ break;
+ default:
+ tt = Terror;
+ fatal(line, "Junk character %c", c);
+ break;
+ }
+ return mktok(tt);
+};
+
+static Tok *number(int base)
+{
+ Tok *t;
+ int start;
+ int c;
+ int isfloat;
+ int unsignedval;
+ /* because we allow '_' in numbers, and strtod/stroull don't, we
+ * need a buffer that holds the number without '_'.
+ */
+ char buf[128];
+ size_t nbuf;
+
+ t = NULL;
+ isfloat = 0;
+ start = fidx;
+ nbuf = 0;
+ for (c = peek(); isxdigit(c) || c == '.' || c == '_'; c = peek()) {
+ next();
+ if (c == '_')
+ continue;
+ if (c == '.')
+ isfloat = 1;
+ else if (hexval(c) < 0 || hexval(c) > base)
+ fatal(line, "Integer digit '%c' outside of base %d", c, base);
+ if (nbuf >= sizeof buf) {
+ buf[nbuf-1] = '\0';
+ fatal(line, "number %s... too long to represent", buf);
+ }
+ buf[nbuf++] = c;
+ }
+ buf[nbuf] = '\0';
+
+ /* we only support base 10 floats */
+ if (isfloat && base == 10) {
+ t = mktok(Tfloatlit);
+ t->str = strdupn(&fbuf[start], fidx - start);
+ t->fltval = strtod(buf, NULL);
+ } else {
+ t = mktok(Tintlit);
+ t->str = strdupn(&fbuf[start], fidx - start);
+ t->intval = strtoull(buf, NULL, base);
+ /* check suffixes:
+ * u -> unsigned
+ * l -> 64 bit
+ * i -> 32 bit
+ * w -> 16 bit
+ * b -> 8 bit
+ */
+ unsignedval = 0;
+nextsuffix:
+ switch (peek()) {
+ case 'u':
+ if (unsignedval == 1)
+ fatal(line, "Duplicate 'u' integer specifier");
+ next();
+ unsignedval = 1;
+ goto nextsuffix;
+ case 'l':
+ next();
+ if (unsignedval)
+ t->inttype = Tyuint64;
+ else
+ t->inttype = Tyint64;
+ break;
+ case 'i':
+ next();
+ if (unsignedval)
+ t->inttype = Tyuint32;
+ else
+ t->inttype = Tyint32;
+ break;
+ case 's':
+ next();
+ if (unsignedval)
+ t->inttype = Tyuint16;
+ else
+ t->inttype = Tyint16;
+ break;
+ case 'b':
+ next();
+ if (unsignedval)
+ t->inttype = Tyuint8;
+ else
+ t->inttype = Tyint8;
+ break;
+ default:
+ if (unsignedval)
+ fatal(line, "Unrecognized character int type specifier after 'u'");
+ break;
+ }
+ }
+
+ return t;
+}
+
+static Tok *numlit(void)
+{
+ Tok *t;
+
+ /* check for 0x or 0b prefix */
+ if (match('0')) {
+ if (match('x'))
+ t = number(16);
+ else if (match('b'))
+ t = number(2);
+ else if (match('o'))
+ t = number(8);
+ else
+ t = number(10);
+ } else {
+ t = number(10);
+ }
+
+ return t;
+}
+
+static Tok *typaram()
+{
+ Tok *t;
+ char buf[1024];
+
+ t = NULL;
+ if (!match('@'))
+ return NULL;
+ if (!identstr(buf, 1024))
+ return NULL;
+ t = mktok(Ttyparam);
+ t->str = strdup(buf);
+ return t;
+}
+
+static Tok *toknext()
+{
+ Tok *t;
+ int c;
+
+ eatspace();
+ c = peek();
+ if (c == End) {
+ t = mktok(0);
+ } else if (c == '\n') {
+ line++;
+ next();
+ t = mktok(Tendln);
+ } else if (isalpha(c) || c == '_') {
+ t = kwident();
+ } else if (c == '"') {
+ t = strlit();
+ } else if (c == '\'') {
+ t = charlit();
+ } else if (isdigit(c)) {
+ t = numlit();
+ } else if (c == '@') {
+ t = typaram();
+ } else {
+ t = oper();
+ }
+
+ if (!t || t->type == Terror)
+ fatal(line, "Unable to parse token starting with %c", c);
+ return t;
+}
+
+void tokinit(char *file)
+{
+ int fd;
+ int n;
+ int nread;
+
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ err(errno, "Unable to open file %s", file);
+
+ nread = 0;
+ fbuf = malloc(4096);
+ while (1) {
+ n = read(fd, fbuf + nread, 4096);
+ if (n < 0)
+ fatal(errno, "Error reading file %s", file);
+ if (n == 0)
+ break;
+ if (!fbuf)
+ die("Out of memory reading %s", file);
+ nread += n;
+ fbuf = xrealloc(fbuf, nread + 4096);
+ }
+
+ fbufsz = nread;
+ line = 1;
+ fidx = 0;
+ close(fd);
+ filename = strdup(file);
+}
+
+/* Interface to yacc */
+int yylex(void)
+{
+ curtok = toknext();
+ yylval.tok = curtok;
+ return curtok->type;
+}
--- /dev/null
+++ b/parse/trait.def
@@ -1,0 +1,6 @@
+/* Definitions of built in constraints */
+Tc(Tcnum, "numeric") /* arith ops */
+Tc(Tcint, "integral") /* behaves like an int, defaults to int as fallback */
+Tc(Tcfloat, "floating") /* behaves like a float, defaults to float as fallback */
+Tc(Tcidx, "indexable") /* indexable */
+Tc(Tcslice, "sliceable") /* sliceable */
--- /dev/null
+++ b/parse/type.c
@@ -1,0 +1,696 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+
+typedef struct Typename Typename;
+struct Typename {
+ Ty ty;
+ char *name;
+};
+
+Type **tytab = NULL;
+Type **types = NULL;
+size_t ntypes;
+Trait **traittab;
+size_t ntraittab;
+
+/* Built in type constraints */
+static Trait *traits[Ntypes + 1][4];
+
+Type *mktype(int line, Ty ty)
+{
+ Type *t;
+ int i;
+
+ /* the first 'n' types will be identity mapped: tytab[Tyint], eg,
+ * will map to an instantitaion of Tyint.
+ *
+ * This is accomplished at program startup by calling mktype() on
+ * each builtin type in order. As we do this, we put the type into
+ * the table as ususal, which gives us an identity mapping.
+ */
+ if (ty <= Tyvalist && ty < ntypes)
+ return types[ty];
+
+ t = zalloc(sizeof(Type));
+ t->type = ty;
+ t->tid = ntypes++;
+ t->line = line;
+ tytab = xrealloc(tytab, ntypes*sizeof(Type*));
+ tytab[t->tid] = NULL;
+ types = xrealloc(types, ntypes*sizeof(Type*));
+ types[t->tid] = t;
+ if (ty <= Tyvalist) /* the last builtin atomic type */
+ t->vis = Visbuiltin;
+
+ for(i = 0; traits[ty][i]; i++)
+ settrait(t, traits[ty][i]);
+
+ return t;
+}
+
+/*
+ * Shallowly duplicates a type, so we can frob
+ * its internals later
+ */
+Type *tydup(Type *t)
+{
+ Type *r;
+
+ r = mktype(t->line, t->type);
+ r->resolved = 0; /* re-resolving doesn't hurt */
+ r->fixed = 0; /* re-resolving doesn't hurt */
+
+ r->traits = bsdup(t->traits);
+ r->traitlist = memdup(t->traitlist, t->ntraitlist * sizeof(Node*));
+ r->ntraitlist = t->ntraitlist;
+
+ r->arg = memdup(t->arg, t->narg * sizeof(Type*));
+ r->narg = t->narg;
+ r->inst = memdup(t->arg, t->narg * sizeof(Type*));
+ r->ninst = t->ninst;
+
+ r->sub = memdup(t->sub, t->nsub * sizeof(Type*));
+ r->nsub = t->nsub;
+ r->nmemb = t->nmemb;
+ switch (t->type) {
+ case Tyname: r->name = t->name; break;
+ case Tyunres: r->name = t->name; break;
+ case Tyarray: r->asize = t->asize; break;
+ case Typaram: r->pname = strdup(t->pname); break;
+ case Tystruct: r->sdecls = memdup(t->sdecls, t->nmemb*sizeof(Node*)); break;
+ case Tyunion: r->udecls = memdup(t->udecls, t->nmemb*sizeof(Node*)); break;
+ default: break;
+ }
+ return r;
+}
+
+/*
+ * Creates a Tyvar with the same
+ * constrants as the 'like' type
+ */
+Type *mktylike(int line, Ty like)
+{
+ Type *t;
+ int i;
+
+ t = mktyvar(line);
+ for (i = 0; traits[like][i]; i++)
+ settrait(t, traits[like][i]);
+ return t;
+}
+
+/* steals memb, funcs */
+Trait *mktrait(int line, Node *name, Type *param, Node **memb, size_t nmemb, Node **funcs, size_t nfuncs, int isproto)
+{
+ Trait *t;
+
+ t = zalloc(sizeof(Trait));
+ t->vis = Visintern;
+ t->name = name;
+ t->param = param;
+ t->memb = memb;
+ t->nmemb = nmemb;
+ t->funcs = funcs;
+ t->nfuncs = nfuncs;
+ t->isproto = isproto;
+ t->uid = ntraittab++;
+
+ traittab = xrealloc(traittab, ntraittab*sizeof(Trait*));
+ traittab[t->uid] = t;
+ return t;
+}
+
+Type *mktyvar(int line)
+{
+ Type *t;
+
+ t = mktype(line, Tyvar);
+ return t;
+}
+
+Type *mktyparam(int line, char *name)
+{
+ Type *t;
+
+ t = mktype(line, Typaram);
+ t->pname = strdup(name);
+ return t;
+}
+
+Type *mktyunres(int line, Node *name, Type **arg, size_t narg)
+{
+ Type *t;
+
+ /* resolve it in the type inference stage */
+ t = mktype(line, Tyunres);
+ t->name = name;
+ t->arg = arg;
+ t->narg = narg;
+ return t;
+}
+
+Type *mktyname(int line, Node *name, Type **param, size_t nparam, Type *base)
+{
+ Type *t;
+
+ t = mktype(line, Tyname);
+ t->name = name;
+ t->nsub = 1;
+ t->traits = bsdup(base->traits);
+ t->sub = xalloc(sizeof(Type*));
+ t->sub[0] = base;
+ t->param = param;
+ t->nparam = nparam;
+ return t;
+}
+
+Type *mktyarray(int line, Type *base, Node *sz)
+{
+ Type *t;
+
+ t = mktype(line, Tyarray);
+ t->nsub = 1;
+ t->nmemb = 1; /* the size is a "member" */
+ t->sub = xalloc(sizeof(Type*));
+ t->sub[0] = base;
+ t->asize = sz;
+
+ return t;
+}
+
+Type *mktyslice(int line, Type *base)
+{
+ Type *t;
+
+ t = mktype(line, Tyslice);
+ t->nsub = 1;
+ t->sub = xalloc(sizeof(Type*));
+ t->sub[0] = base;
+ return t;
+}
+
+Type *mktyidxhack(int line, Type *base)
+{
+ Type *t;
+
+ t = mktype(line, Tyvar);
+ t->nsub = 1;
+ t->sub = xalloc(sizeof(Type*));
+ t->sub[0] = base;
+ return t;
+}
+
+Type *mktyptr(int line, Type *base)
+{
+ Type *t;
+
+ t = mktype(line, Typtr);
+ t->nsub = 1;
+ t->sub = xalloc(sizeof(Type*));
+ t->sub[0] = base;
+ return t;
+}
+
+Type *mktytuple(int line, Type **sub, size_t nsub)
+{
+ Type *t;
+ size_t i;
+
+ t = mktype(line, Tytuple);
+ t->nsub = nsub;
+ t->sub = xalloc(nsub*sizeof(Type));
+ for (i = 0; i < nsub; i++)
+ t->sub[i] = sub[i];
+ return t;
+}
+
+Type *mktyfunc(int line, Node **args, size_t nargs, Type *ret)
+{
+ Type *t;
+ size_t i;
+
+ t = mktype(line, Tyfunc);
+ t->nsub = nargs + 1;
+ t->sub = xalloc((1 + nargs)*sizeof(Type));
+ t->sub[0] = ret;
+ for (i = 0; i < nargs; i++)
+ t->sub[i + 1] = nodetype(args[i]);
+ return t;
+}
+
+Type *mktystruct(int line, Node **decls, size_t ndecls)
+{
+ Type *t;
+
+ t = mktype(line, Tystruct);
+ t->nsub = 0;
+ t->nmemb = ndecls;
+ t->sdecls = memdup(decls, ndecls*sizeof(Node *));
+ return t;
+}
+
+Type *mktyunion(int line, Ucon **decls, size_t ndecls)
+{
+ Type *t;
+
+ t = mktype(line, Tyunion);
+ t->nmemb = ndecls;
+ t->udecls = decls;
+ return t;
+}
+
+int istysigned(Type *t)
+{
+ switch (tybase(t)->type) {
+ case Tyint8: case Tyint16: case Tyint:
+ case Tyint32: case Tyint64: case Tylong:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int istyfloat(Type *t)
+{
+ switch (tybase(t)->type) {
+ case Tyfloat32: case Tyfloat64:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int isgeneric(Type *t)
+{
+ size_t i;
+
+ if (t->type != Tyname && t->type != Tyunres)
+ return 0;
+ if (!t->narg)
+ return t->nparam > 0;
+ else
+ for (i = 0; i < t->narg; i++)
+ if (hasparams(t->arg[i]))
+ return 1;
+ return 0;
+}
+
+/*
+ * Checks if a type contains any type
+ * parameers at all (ie, if it generic).
+ */
+int hasparams(Type *t)
+{
+ size_t i;
+
+ if (t->type == Typaram || isgeneric(t))
+ return 1;
+ for (i = 0; i < t->nsub; i++)
+ if (hasparams(t->sub[i]))
+ return 1;
+ for (i = 0; i < t->narg; i++)
+ if (hasparams(t->arg[i]))
+ return 1;
+ return 0;
+}
+
+Type *tybase(Type *t)
+{
+ assert(t != NULL);
+ while (t->type == Tyname)
+ t = t->sub[0];
+ return t;
+}
+
+
+static int namefmt(char *buf, size_t len, Node *n)
+{
+ char *p;
+ char *end;
+
+ p = buf;
+ end = p + len;
+ if (n->name.ns)
+ p += snprintf(p, end - p, "%s.", n->name.ns);
+ p += snprintf(p, end - p, "%s", n->name.name);
+ return len - (end - p);
+}
+
+int settrait(Type *t, Trait *c)
+{
+ if (!t->traits)
+ t->traits = mkbs();
+ bsput(t->traits, c->uid);
+ return 1;
+}
+
+int hastrait(Type *t, Trait *c)
+{
+ return t->traits && bshas(t->traits, c->uid);
+}
+
+int traitfmt(char *buf, size_t len, Type *t)
+{
+ size_t i;
+ char *p;
+ char *end;
+ char *sep;
+
+ if (!t->traits || !bscount(t->traits))
+ return 0;
+
+ p = buf;
+ end = p + len;
+
+ p += snprintf(p, end - p, " :: ");
+ sep = "";
+ for (i = 0; i < ntraittab; i++) {
+ if (bshas(t->traits, i)) {
+ p += snprintf(p, end - p, "%s%s", sep, namestr(traittab[i]->name));
+ sep = ",";
+ }
+ }
+ return p - buf;
+}
+
+static int fmtstruct(char *buf, size_t len, Type *t)
+{
+ size_t i;
+ char *end, *p;
+ char *name, *ty;
+
+ p = buf;
+ end = p + len;
+ p += snprintf(p, end - p, "struct ");
+ for (i = 0; i < t->nmemb; i++) {
+ name = declname(t->sdecls[i]);
+ ty = tystr(decltype(t->sdecls[i]));
+ p += snprintf(p, end - p, "%s:%s; ", name, ty);
+ free(ty);
+ }
+ p += snprintf(p, end - p, ";;");
+ return p - buf;
+}
+
+static int fmtunion(char *buf, size_t len, Type *t)
+{
+ size_t i;
+ char *end, *p;
+ char *name, *ty;
+
+ p = buf;
+ end = p + len;
+ p += snprintf(p, end - p, "union ");
+ for (i = 0; i < t->nmemb; i++) {
+ name = namestr(t->udecls[i]->name);
+ ty = tystr(t->udecls[i]->etype);
+ p += snprintf(p, end - p, "`%s %s; ", name, ty);
+ free(ty);
+ }
+ p += snprintf(p, end - p, ";;");
+ return p - buf;
+}
+
+static int tybfmt(char *buf, size_t len, Type *t)
+{
+ size_t i;
+ char *p;
+ char *end;
+ char *sep;
+ size_t narg;
+ Type **arg;
+
+ p = buf;
+ end = p + len;
+ sep = "";
+ if (!t) {
+ p += snprintf(p, end - p, "tynil");
+ return len - (end - p);
+ }
+ switch (t->type) {
+ case Tybad: p += snprintf(p, end - p, "BAD"); break;
+ case Tyvoid: p += snprintf(p, end - p, "void"); break;
+ case Tybool: p += snprintf(p, end - p, "bool"); break;
+ case Tychar: p += snprintf(p, end - p, "char"); break;
+ case Tyint8: p += snprintf(p, end - p, "int8"); break;
+ case Tyint16: p += snprintf(p, end - p, "int16"); break;
+ case Tyint: p += snprintf(p, end - p, "int"); break;
+ case Tyint32: p += snprintf(p, end - p, "int32"); break;
+ case Tyint64: p += snprintf(p, end - p, "int64"); break;
+ case Tylong: p += snprintf(p, end - p, "long"); break;
+ case Tybyte: p += snprintf(p, end - p, "byte"); break;
+ case Tyuint8: p += snprintf(p, end - p, "uint8"); break;
+ case Tyuint16: p += snprintf(p, end - p, "uint16"); break;
+ case Tyuint: p += snprintf(p, end - p, "uint"); break;
+ case Tyuint32: p += snprintf(p, end - p, "uint32"); break;
+ case Tyuint64: p += snprintf(p, end - p, "uint64"); break;
+ case Tyulong: p += snprintf(p, end - p, "ulong"); break;
+ case Tyfloat32: p += snprintf(p, end - p, "float32"); break;
+ case Tyfloat64: p += snprintf(p, end - p, "float64"); break;
+ case Tyvalist: p += snprintf(p, end - p, "..."); break;
+
+ case Typtr:
+ p += tybfmt(p, end - p, t->sub[0]);
+ p += snprintf(p, end - p, "#");
+ break;
+ case Tyslice:
+ p += tybfmt(p, end - p, t->sub[0]);
+ p += snprintf(p, end - p, "[:]");
+ break;
+ case Tyarray:
+ p += tybfmt(p, end - p, t->sub[0]);
+ p += snprintf(p, end - p, "[%llu]", t->asize->expr.args[0]->lit.intval);
+ break;
+ case Tyfunc:
+ p += snprintf(p, end - p, "(");
+ for (i = 1; i < t->nsub; i++) {
+ p += snprintf(p, end - p, "%s", sep);
+ p += tybfmt(p, end - p, t->sub[i]);
+ sep = ", ";
+ }
+ p += snprintf(p, end - p, " -> ");
+ p += tybfmt(p, end - p, t->sub[0]);
+ p += snprintf(p, end - p, ")");
+ break;
+ case Tytuple:
+ p += snprintf(p, end - p, "[");
+ for (i = 0; i < t->nsub; i++) {
+ p += snprintf(p, end - p, "%s", sep);
+ p += tybfmt(p, end - p, t->sub[i]);
+ sep = ", ";
+ }
+ p += snprintf(p, end - p, "]");
+ break;
+ case Tyvar:
+ p += snprintf(p, end - p, "$%d", t->tid);
+ if (t->nsub) {
+ p += snprintf(p, end - p, "(");
+ for (i = 0; i < t->nsub; i++) {
+ p += snprintf(p, end - p, "%s", sep);
+ p += tybfmt(p, end - p, t->sub[i]);
+ sep = ", ";
+ }
+ p += snprintf(p, end - p, ")[]");
+ }
+ break;
+ case Typaram:
+ p += snprintf(p, end - p, "@%s", t->pname);
+ break;
+ case Tyunres:
+ p += snprintf(p, end - p, "?"); /* indicate unresolved name. should not be seen by user. */
+ p += namefmt(p, end - p, t->name);
+ if (t->narg) {
+ p += snprintf(p, end - p, "(");
+ for (i = 0; i < t->narg; i++) {
+ p += snprintf(p, end - p, "%s", sep);
+ p += tybfmt(p, end - p, t->arg[i]);
+ sep = ", ";
+ }
+ p += snprintf(p, end - p, ")");
+ }
+ break;
+ case Tyname:
+ if (t->name->name.ns)
+ p += snprintf(p, end - p, "%s.", t->name->name.ns);
+ p += snprintf(p, end - p, "%s", namestr(t->name));
+ if (t->narg) {
+ arg = t->arg;
+ narg = t->narg;
+ } else {
+ arg = t->param;
+ narg = t->nparam;
+ }
+ if (!narg)
+ break;
+ p += snprintf(p, end - p, "(");
+ for (i = 0; i < narg; i++) {
+ p += snprintf(p, end - p, "%s", sep);
+ p += tybfmt(p, end - p, arg[i]);
+ sep = ", ";
+ }
+ p += snprintf(p, end - p, ")");
+ break;
+ case Tystruct: p += fmtstruct(p, end - p, t); break;
+ case Tyunion: p += fmtunion(p, end - p, t); break;
+ case Ntypes:
+ die("Ntypes is not a type");
+ break;
+ }
+
+ /* we only show constraints on non-builtin typaram */
+ if (t->type == Tyvar || t->type == Typaram)
+ p += traitfmt(p, end - p, t);
+
+ return p - buf;
+}
+
+char *tyfmt(char *buf, size_t len, Type *t)
+{
+ tybfmt(buf, len, t);
+ return buf;
+}
+
+char *traitstr(Type *t)
+{
+ char buf[1024];
+ traitfmt(buf, 1024, t);
+ return strdup(buf);
+}
+
+char *tystr(Type *t)
+{
+ char buf[1024];
+ tyfmt(buf, 1024, t);
+ return strdup(buf);
+}
+
+ulong tyhash(void *ty)
+{
+ size_t i;
+ Type *t;
+ ulong hash;
+
+ t = (Type *)ty;
+ if (t->type == Typaram)
+ hash = strhash(t->pname);
+ else
+ hash = inthash(t->tid);
+
+ for (i = 0; i < t->narg; i++)
+ hash ^= tyhash(t->arg[i]);
+ return hash;
+}
+
+int tyeq(void *t1, void *t2)
+{
+ Type *a, *b;
+ size_t i;
+
+ a = (Type *)t1;
+ b = (Type *)t2;
+ if (a == b)
+ return 1;
+ if (a->type != b->type)
+ return 0;
+ if (a->tid == b->tid)
+ return 1;
+ if (a->narg != b->narg)
+ return 0;
+ if (a->nsub != b->nsub)
+ return 0;
+ if (a->nmemb != b->nmemb)
+ return 0;
+ switch (a->type) {
+ case Typaram:
+ return streq(a->pname, b->pname);
+ break;
+ case Tyunion:
+ for (i = 0; i < a->nmemb; i++)
+ if (!tyeq(a->udecls[i]->etype, b->udecls[i]->etype))
+ return 0;
+ break;
+ case Tyname:
+ if (!nameeq(a->name, b->name))
+ return 0;
+ for (i = 0; i < a->narg; i++)
+ if (!tyeq(a->arg[i], b->arg[i]))
+ return 0;
+ for (i = 0; i < a->nsub; i++)
+ if (!tyeq(a->sub[i], b->sub[i]))
+ return 0;
+ break;
+ default:
+ for (i = 0; i < a->nsub; i++)
+ if (!tyeq(a->sub[i], b->sub[i]))
+ return 0;
+ break;
+ }
+ return 1;
+}
+
+void tyinit(Stab *st)
+{
+ int i;
+ Type *ty;
+
+/* this must be done after all the types are created, otherwise we will
+ * clobber the memoized bunch of types with the type params. */
+#define Tc(c, n) \
+ mktrait(-1, mkname(-1, n), NULL, NULL, 0, NULL, 0, 0);
+#include "trait.def"
+#undef Tc
+
+ /* char::(numeric,integral) */
+ traits[Tychar][0] = traittab[Tcnum];
+ traits[Tychar][1] = traittab[Tcint];
+
+ traits[Tybyte][0] = traittab[Tcnum];
+ traits[Tybyte][1] = traittab[Tcint];
+
+ /* <integer types>::(numeric,integral) */
+ for (i = Tyint8; i < Tyfloat32; i++) {
+ traits[i][0] = traittab[Tcnum];
+ traits[i][1] = traittab[Tcint];
+ }
+
+ /* <floats>::(numeric,floating) */
+ traits[Tyfloat32][0] = traittab[Tcnum];
+ traits[Tyfloat32][1] = traittab[Tcfloat];
+ traits[Tyfloat64][0] = traittab[Tcnum];
+ traits[Tyfloat64][1] = traittab[Tcfloat];
+
+ /* @a*::(sliceable) */
+ traits[Typtr][0] = traittab[Tcslice];
+
+ /* @a[:]::(indexable,sliceable) */
+ traits[Tyslice][0] = traittab[Tcslice];
+ traits[Tyslice][1] = traittab[Tcidx];
+
+ /* @a[SZ]::(indexable,sliceable) */
+ traits[Tyarray][0] = traittab[Tcidx];
+ traits[Tyarray][1] = traittab[Tcslice];
+
+/* Definining and registering the types has to go after we define the
+ * constraints, otherwise they will have no constraints set on them. */
+#define Ty(t, n) \
+ if (t != Ntypes) {\
+ ty = mktype(-1, t); \
+ if (n) { \
+ puttype(st, mkname(-1, n), ty); \
+ } \
+ }
+#include "types.def"
+#undef Ty
+
+}
--- /dev/null
+++ b/parse/types.def
@@ -1,0 +1,46 @@
+Ty(Tybad, NULL)
+Ty(Tyvoid, "void")
+
+/* start integer types.
+ * Keep them ordered between start
+ * and end for faster
+ * comparisons.*/
+Ty(Tybool, "bool")
+Ty(Tychar, "char")
+
+Ty(Tyint8, "int8")
+Ty(Tyint16, "int16")
+Ty(Tyint, "int")
+Ty(Tyint32, "int32")
+Ty(Tyint64, "int64")
+Ty(Tylong, "long")
+
+Ty(Tybyte, "byte")
+Ty(Tyuint8, "uint8")
+Ty(Tyuint16, "uint16")
+Ty(Tyuint, "uint")
+Ty(Tyuint32, "uint32")
+Ty(Tyuint64, "uint64")
+Ty(Tyulong, "ulong")
+/*end integer types*/
+Ty(Tyfloat32, "float32")
+Ty(Tyfloat64, "float64")
+/* end primitive types */
+Ty(Tyvalist, NULL)
+
+/* end atomic types */
+Ty(Typtr, NULL)
+Ty(Tyfunc, NULL)
+
+/* these types live on the stack */
+Ty(Tyslice, NULL)
+Ty(Tyarray, NULL)
+Ty(Tytuple, NULL)
+Ty(Tystruct, NULL)
+Ty(Tyunion, NULL)
+
+/* these have no memory repr */
+Ty(Tyvar, NULL)
+Ty(Typaram, NULL)
+Ty(Tyunres, NULL) /* unresolved */
+Ty(Tyname, NULL)
--- /dev/null
+++ b/parse/use.c
@@ -1,0 +1,931 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+
+static void wrtype(FILE *fd, Type *val);
+static void rdtype(FILE *fd, Type **dest);
+static void wrstab(FILE *fd, Stab *val);
+static Stab *rdstab(FILE *fd);
+static void wrsym(FILE *fd, Node *val);
+static Node *rdsym(FILE *fd, Trait *ctx);
+static void pickle(FILE *fd, Node *n);
+static Node *unpickle(FILE *fd);
+
+/* type fixup list */
+static Htab *tydedup; /* map from name -> type, contains all Tynames loaded ever */
+static Htab *tidmap; /* map from tid -> type */
+static Htab *trmap; /* map from trait id -> trait */
+static Type ***typefixdest; /* list of types we need to replace */
+static size_t ntypefixdest; /* size of replacement list */
+static intptr_t *typefixid; /* list of types we need to replace */
+static size_t ntypefixid; /* size of replacement list */
+#define Builtinmask (1 << 30)
+
+/* Outputs a symbol table to file in a way that can be
+ * read back usefully. Only writes declarations, types
+ * and sub-namespaces. Captured variables are ommitted. */
+static void wrstab(FILE *fd, Stab *val)
+{
+ size_t n, i;
+ void **keys;
+
+ pickle(fd, val->name);
+
+ /* write decls */
+ keys = htkeys(val->dcl, &n);
+ wrint(fd, n);
+ for (i = 0; i < n; i++)
+ wrsym(fd, getdcl(val, keys[i]));
+ free(keys);
+
+ /* write types */
+ keys = htkeys(val->ty, &n);
+ wrint(fd, n);
+ for (i = 0; i < n; i++) {
+ pickle(fd, keys[i]); /* name */
+ wrtype(fd, gettype(val, keys[i])); /* type */
+ }
+ free(keys);
+
+ /* write stabs */
+ keys = htkeys(val->ns, &n);
+ wrint(fd, n);
+ for (i = 0; i < n; i++)
+ wrstab(fd, getns(val, keys[i]));
+ free(keys);
+}
+
+/* Reads a symbol table from file. The converse
+ * of wrstab. */
+static Stab *rdstab(FILE *fd)
+{
+ Stab *st;
+ Type *ty;
+ Node *nm;
+ int n;
+ int i;
+
+ /* read dcls */
+ st = mkstab();
+ st->name = unpickle(fd);
+ n = rdint(fd);
+ for (i = 0; i < n; i++)
+ putdcl(st, rdsym(fd, NULL));
+
+ /* read types */
+ n = rdint(fd);
+ for (i = 0; i < n; i++) {
+ nm = unpickle(fd);
+ rdtype(fd, &ty);
+ puttype(st, nm, ty);
+ }
+
+ /* read stabs */
+ n = rdint(fd);
+ for (i = 0; i < n; i++)
+ putns(st, rdstab(fd));
+
+ return st;
+}
+
+static void wrucon(FILE *fd, Ucon *uc)
+{
+ wrint(fd, uc->line);
+ wrint(fd, uc->id);
+ wrbool(fd, uc->synth);
+ pickle(fd, uc->name);
+ wrbool(fd, uc->etype != NULL);
+ if (uc->etype)
+ wrtype(fd, uc->etype);
+}
+
+static Ucon *rducon(FILE *fd, Type *ut)
+{
+ Type *et;
+ Node *name;
+ Ucon *uc;
+ size_t id;
+ int line;
+ int synth;
+
+ et = NULL;
+ line = rdint(fd);
+ id = rdint(fd);
+ synth = rdbool(fd);
+ name = unpickle(fd);
+ uc = mkucon(line, name, ut, et);
+ if (rdbool(fd))
+ rdtype(fd, &uc->etype);
+ uc->id = id;
+ uc->synth = synth;
+ return uc;
+}
+
+/* Writes the name and type of a variable,
+ * but only writes its intializer for things
+ * we want to inline cross-file (currently,
+ * the only cross-file inline is generics) */
+static void wrsym(FILE *fd, Node *val)
+{
+ /* sym */
+ wrint(fd, val->line);
+ pickle(fd, val->decl.name);
+ wrtype(fd, val->decl.type);
+
+ /* symflags */
+ wrint(fd, val->decl.vis);
+ wrbool(fd, val->decl.isconst);
+ wrbool(fd, val->decl.isgeneric);
+ wrbool(fd, val->decl.isextern);
+
+ if (val->decl.isgeneric && !val->decl.trait)
+ pickle(fd, val->decl.init);
+}
+
+static Node *rdsym(FILE *fd, Trait *ctx)
+{
+ int line;
+ Node *name;
+ Node *n;
+
+ line = rdint(fd);
+ name = unpickle(fd);
+ n = mkdecl(line, name, NULL);
+ rdtype(fd, &n->decl.type);
+
+ if (rdint(fd) == Vishidden)
+ n->decl.ishidden = 1;
+ n->decl.trait = ctx;
+ n->decl.isconst = rdbool(fd);
+ n->decl.isgeneric = rdbool(fd);
+ n->decl.isextern = rdbool(fd);
+
+ if (n->decl.isgeneric && !ctx)
+ n->decl.init = unpickle(fd);
+ return n;
+}
+
+/* Writes types to a file. Errors on
+ * internal only types like Tyvar that
+ * will not be meaningful in another file*/
+static void typickle(FILE *fd, Type *ty)
+{
+ size_t i;
+
+ if (!ty) {
+ die("trying to pickle null type\n");
+ return;
+ }
+ wrbyte(fd, ty->type);
+ wrbyte(fd, ty->vis);
+ /* tid is generated; don't write */
+ /* FIXME: since we only support hardcoded traits, we just write
+ * out the set of them. we should write out the trait list as
+ * well */
+ if (!ty->traits) {
+ wrint(fd, 0);
+ } else {
+ wrint(fd, bscount(ty->traits));
+ for (i = 0; bsiter(ty->traits, &i); i++)
+ wrint(fd, i);
+ }
+ wrint(fd, ty->nsub);
+ switch (ty->type) {
+ case Tyunres:
+ pickle(fd, ty->name);
+ break;
+ case Typaram:
+ wrstr(fd, ty->pname);
+ break;
+ case Tystruct:
+ wrint(fd, ty->nmemb);
+ for (i = 0; i < ty->nmemb; i++)
+ pickle(fd, ty->sdecls[i]);
+ break;
+ case Tyunion:
+ wrint(fd, ty->nmemb);
+ for (i = 0; i < ty->nmemb; i++)
+ wrucon(fd, ty->udecls[i]);
+ break;
+ case Tyarray:
+ wrtype(fd, ty->sub[0]);
+ pickle(fd, ty->asize);
+ break;
+ case Tyslice:
+ wrtype(fd, ty->sub[0]);
+ break;
+ case Tyvar:
+ die("Attempting to pickle %s. This will not work.\n", tystr(ty));
+ break;
+ case Tyname:
+ pickle(fd, ty->name);
+ wrbool(fd, ty->issynth);
+
+ wrint(fd, ty->nparam);
+ for (i = 0; i < ty->nparam; i++)
+ wrtype(fd, ty->param[i]);
+
+ wrint(fd, ty->narg);
+ for (i = 0; i < ty->narg; i++)
+ wrtype(fd, ty->arg[i]);
+
+ wrtype(fd, ty->sub[0]);
+ break;
+ default:
+ for (i = 0; i < ty->nsub; i++)
+ wrtype(fd, ty->sub[i]);
+ break;
+ }
+}
+
+static void traitpickle(FILE *fd, Trait *tr)
+{
+ size_t i;
+
+ wrint(fd, tr->uid);
+ wrbool(fd, tr->ishidden);
+ pickle(fd, tr->name);
+ typickle(fd, tr->param);
+ wrint(fd, tr->nmemb);
+ for (i = 0; i < tr->nmemb; i++)
+ wrsym(fd, tr->memb[i]);
+ wrint(fd, tr->nfuncs);
+ for (i = 0; i < tr->nfuncs; i++)
+ wrsym(fd, tr->funcs[i]);
+}
+
+static void wrtype(FILE *fd, Type *ty)
+{
+ if (ty->tid >= Builtinmask)
+ die("Type id %d for %s too big", ty->tid, tystr(ty));
+ if (ty->vis == Visbuiltin)
+ wrint(fd, ty->type | Builtinmask);
+ else
+ wrint(fd, ty->tid);
+}
+
+static void rdtype(FILE *fd, Type **dest)
+{
+ intptr_t tid;
+
+ tid = rdint(fd);
+ if (tid & Builtinmask) {
+ *dest = mktype(-1, tid & ~Builtinmask);
+ } else {
+ lappend(&typefixdest, &ntypefixdest, dest);
+ lappend(&typefixid, &ntypefixid, itop(tid));
+ }
+}
+
+/* Writes types to a file. Errors on
+ * internal only types like Tyvar that
+ * will not be meaningful in another file */
+static Type *tyunpickle(FILE *fd)
+{
+ size_t i, n;
+ size_t v;
+ Type *ty;
+ Trait *tr;
+ Ty t;
+
+ t = rdbyte(fd);
+ ty = mktype(-1, t);
+ if (rdbyte(fd) == Vishidden)
+ ty->ishidden = 1;
+ /* tid is generated; don't write */
+ n = rdint(fd);
+ if (n > 0) {
+ ty->traits = mkbs();
+ for (i = 0; i < n; i++) {
+ v = rdint(fd);
+ tr = htget(trmap, itop(v));
+ settrait(ty, tr);
+ }
+ }
+ ty->nsub = rdint(fd);
+ if (ty->nsub > 0)
+ ty->sub = zalloc(ty->nsub * sizeof(Type*));
+ switch (ty->type) {
+ case Tyunres:
+ ty->name = unpickle(fd);
+ break;
+ case Typaram:
+ ty->pname = rdstr(fd);
+ break;
+ case Tystruct:
+ ty->nmemb = rdint(fd);
+ ty->sdecls = zalloc(ty->nmemb * sizeof(Node*));
+ for (i = 0; i < ty->nmemb; i++)
+ ty->sdecls[i] = unpickle(fd);
+ break;
+ case Tyunion:
+ ty->nmemb = rdint(fd);
+ ty->udecls = zalloc(ty->nmemb * sizeof(Node*));
+ for (i = 0; i < ty->nmemb; i++)
+ ty->udecls[i] = rducon(fd, ty);
+ break;
+ case Tyarray:
+ rdtype(fd, &ty->sub[0]);
+ ty->asize = unpickle(fd);
+ break;
+ case Tyslice:
+ rdtype(fd, &ty->sub[0]);
+ break;
+ case Tyname:
+ ty->name = unpickle(fd);
+ ty->issynth = rdbool(fd);
+
+ ty->nparam = rdint(fd);
+ ty->param = zalloc(ty->nparam * sizeof(Type *));
+ for (i = 0; i < ty->nparam; i++)
+ rdtype(fd, &ty->param[i]);
+
+ ty->narg = rdint(fd);
+ ty->arg = zalloc(ty->narg * sizeof(Type *));
+ for (i = 0; i < ty->narg; i++)
+ rdtype(fd, &ty->arg[i]);
+
+ rdtype(fd, &ty->sub[0]);
+ break;
+ default:
+ for (i = 0; i < ty->nsub; i++)
+ rdtype(fd, &ty->sub[i]);
+ break;
+ }
+ return ty;
+}
+
+Trait *traitunpickle(FILE *fd)
+{
+ Trait *tr;
+ size_t i, n;
+ intptr_t uid;
+
+ /* create an empty trait */
+ tr = mktrait(-1, NULL, NULL, NULL, 0, NULL, 0, 0);
+ uid = rdint(fd);
+ tr->ishidden = rdbool(fd);
+ tr->name = unpickle(fd);
+ tr->param = tyunpickle(fd);
+ n = rdint(fd);
+ for (i = 0; i < n; i++)
+ lappend(&tr->memb, &tr->nmemb, rdsym(fd, tr));
+ n = rdint(fd);
+ for (i = 0; i < n; i++)
+ lappend(&tr->funcs, &tr->nfuncs, rdsym(fd, tr));
+ htput(trmap, itop(uid), tr);
+ return tr;
+}
+
+/* Pickles a node to a file. The format
+ * is more or less equivalen to to
+ * simplest serialization of the
+ * in-memory representation. Minimal
+ * checking is done, so a bad type can
+ * crash the compiler */
+static void pickle(FILE *fd, Node *n)
+{
+ size_t i;
+
+ if (!n) {
+ wrbyte(fd, Nnone);
+ return;
+ }
+ wrbyte(fd, n->type);
+ wrint(fd, n->line);
+ switch (n->type) {
+ case Nfile:
+ wrstr(fd, n->file.name);
+ wrint(fd, n->file.nuses);
+ for (i = 0; i < n->file.nuses; i++)
+ pickle(fd, n->file.uses[i]);
+ wrint(fd, n->file.nstmts);
+ for (i = 0; i < n->file.nstmts; i++)
+ pickle(fd, n->file.stmts[i]);
+ wrstab(fd, n->file.globls);
+ wrstab(fd, n->file.exports);
+ break;
+
+ case Nexpr:
+ wrbyte(fd, n->expr.op);
+ wrtype(fd, n->expr.type);
+ wrbool(fd, n->expr.isconst);
+ pickle(fd, n->expr.idx);
+ wrint(fd, n->expr.nargs);
+ for (i = 0; i < n->expr.nargs; i++)
+ pickle(fd, n->expr.args[i]);
+ break;
+ case Nname:
+ wrbool(fd, n->name.ns != NULL);
+ if (n->name.ns) {
+ wrstr(fd, n->name.ns);
+ }
+ wrstr(fd, n->name.name);
+ break;
+ case Nuse:
+ wrbool(fd, n->use.islocal);
+ wrstr(fd, n->use.name);
+ break;
+ case Nlit:
+ wrbyte(fd, n->lit.littype);
+ wrtype(fd, n->lit.type);
+ wrint(fd, n->lit.nelt);
+ switch (n->lit.littype) {
+ case Lchr: wrint(fd, n->lit.chrval); break;
+ case Lint: wrint(fd, n->lit.intval); break;
+ case Lflt: wrflt(fd, n->lit.fltval); break;
+ case Lstr: wrstr(fd, n->lit.strval); break;
+ case Llbl: wrstr(fd, n->lit.lblval); break;
+ case Lbool: wrbool(fd, n->lit.boolval); break;
+ case Lfunc: pickle(fd, n->lit.fnval); break;
+ }
+ break;
+ case Nloopstmt:
+ pickle(fd, n->loopstmt.init);
+ pickle(fd, n->loopstmt.cond);
+ pickle(fd, n->loopstmt.step);
+ pickle(fd, n->loopstmt.body);
+ break;
+ case Niterstmt:
+ pickle(fd, n->iterstmt.elt);
+ pickle(fd, n->iterstmt.seq);
+ pickle(fd, n->iterstmt.body);
+ break;
+ case Nmatchstmt:
+ pickle(fd, n->matchstmt.val);
+ wrint(fd, n->matchstmt.nmatches);
+ for (i = 0; i < n->matchstmt.nmatches; i++)
+ pickle(fd, n->matchstmt.matches[i]);
+ break;
+ case Nmatch:
+ pickle(fd, n->match.pat);
+ pickle(fd, n->match.block);
+ break;
+ case Nifstmt:
+ pickle(fd, n->ifstmt.cond);
+ pickle(fd, n->ifstmt.iftrue);
+ pickle(fd, n->ifstmt.iffalse);
+ break;
+ case Nblock:
+ wrstab(fd, n->block.scope);
+ wrint(fd, n->block.nstmts);
+ for (i = 0; i < n->block.nstmts; i++)
+ pickle(fd, n->block.stmts[i]);
+ break;
+ case Ndecl:
+ /* sym */
+ pickle(fd, n->decl.name);
+ wrtype(fd, n->decl.type);
+
+ /* symflags */
+ wrint(fd, n->decl.isconst);
+ wrint(fd, n->decl.isgeneric);
+ wrint(fd, n->decl.isextern);
+
+ /* init */
+ pickle(fd, n->decl.init);
+ break;
+ case Nfunc:
+ wrtype(fd, n->func.type);
+ wrstab(fd, n->func.scope);
+ wrint(fd, n->func.nargs);
+ for (i = 0; i < n->func.nargs; i++)
+ pickle(fd, n->func.args[i]);
+ pickle(fd, n->func.body);
+ break;
+ case Nimpl:
+ pickle(fd, n->impl.traitname);
+ wrint(fd, n->impl.trait->uid);
+ wrtype(fd, n->impl.type);
+ wrint(fd, n->impl.ndecls);
+ for (i = 0; i < n->impl.ndecls; i++)
+ wrsym(fd, n->impl.decls[i]);
+ break;
+ case Nnone:
+ die("Nnone should not be seen as node type!");
+ break;
+ }
+}
+
+/* Unpickles a node from a file. Minimal checking
+ * is done. Specifically, no checks are done for
+ * sane arities, a bad file can crash the compiler */
+static Node *unpickle(FILE *fd)
+{
+ size_t i;
+ Ntype type;
+ Node *n;
+
+ type = rdbyte(fd);
+ if (type == Nnone)
+ return NULL;
+ n = mknode(-1, type);
+ n->line = rdint(fd);
+ switch (n->type) {
+ case Nfile:
+ n->file.name = rdstr(fd);
+ n->file.nuses = rdint(fd);
+ n->file.uses = zalloc(sizeof(Node*)*n->file.nuses);
+ for (i = 0; i < n->file.nuses; i++)
+ n->file.uses[i] = unpickle(fd);
+ n->file.nstmts = rdint(fd);
+ n->file.stmts = zalloc(sizeof(Node*)*n->file.nstmts);
+ for (i = 0; i < n->file.nstmts; i++)
+ n->file.stmts[i] = unpickle(fd);
+ n->file.globls = rdstab(fd);
+ n->file.exports = rdstab(fd);
+ break;
+
+ case Nexpr:
+ n->expr.op = rdbyte(fd);
+ rdtype(fd, &n->expr.type);
+ n->expr.isconst = rdbool(fd);
+ n->expr.idx = unpickle(fd);
+ n->expr.nargs = rdint(fd);
+ n->expr.args = zalloc(sizeof(Node *)*n->expr.nargs);
+ for (i = 0; i < n->expr.nargs; i++)
+ n->expr.args[i] = unpickle(fd);
+ break;
+ case Nname:
+ if (rdbool(fd))
+ n->name.ns = rdstr(fd);
+ n->name.name = rdstr(fd);
+ break;
+ case Nuse:
+ n->use.islocal = rdbool(fd);
+ n->use.name = rdstr(fd);
+ break;
+ case Nlit:
+ n->lit.littype = rdbyte(fd);
+ rdtype(fd, &n->lit.type);
+ n->lit.nelt = rdint(fd);
+ switch (n->lit.littype) {
+ case Lchr: n->lit.chrval = rdint(fd); break;
+ case Lint: n->lit.intval = rdint(fd); break;
+ case Lflt: n->lit.fltval = rdflt(fd); break;
+ case Lstr: n->lit.strval = rdstr(fd); break;
+ case Llbl: n->lit.lblval = rdstr(fd); break;
+ case Lbool: n->lit.boolval = rdbool(fd); break;
+ case Lfunc: n->lit.fnval = unpickle(fd); break;
+ }
+ break;
+ case Nloopstmt:
+ n->loopstmt.init = unpickle(fd);
+ n->loopstmt.cond = unpickle(fd);
+ n->loopstmt.step = unpickle(fd);
+ n->loopstmt.body = unpickle(fd);
+ break;
+ case Niterstmt:
+ n->iterstmt.elt = unpickle(fd);
+ n->iterstmt.seq = unpickle(fd);
+ n->iterstmt.body = unpickle(fd);
+ break;
+ case Nmatchstmt:
+ n->matchstmt.val = unpickle(fd);
+ n->matchstmt.nmatches = rdint(fd);
+ n->matchstmt.matches = zalloc(sizeof(Node *)*n->matchstmt.nmatches);
+ for (i = 0; i < n->matchstmt.nmatches; i++)
+ n->matchstmt.matches[i] = unpickle(fd);
+ break;
+ case Nmatch:
+ n->match.pat = unpickle(fd);
+ n->match.block = unpickle(fd);
+ break;
+ case Nifstmt:
+ n->ifstmt.cond = unpickle(fd);
+ n->ifstmt.iftrue = unpickle(fd);
+ n->ifstmt.iffalse = unpickle(fd);
+ break;
+ case Nblock:
+ n->block.scope = rdstab(fd);
+ n->block.nstmts = rdint(fd);
+ n->block.stmts = zalloc(sizeof(Node *)*n->block.nstmts);
+ n->block.scope->super = curstab();
+ pushstab(n->func.scope->super);
+ for (i = 0; i < n->block.nstmts; i++)
+ n->block.stmts[i] = unpickle(fd);
+ popstab();
+ break;
+ case Ndecl:
+ n->decl.did = ndecls; /* unique within file */
+ /* sym */
+ n->decl.name = unpickle(fd);
+ rdtype(fd, &n->decl.type);
+
+ /* symflags */
+ n->decl.isconst = rdint(fd);
+ n->decl.isgeneric = rdint(fd);
+ n->decl.isextern = rdint(fd);
+
+ /* init */
+ n->decl.init = unpickle(fd);
+ lappend(&decls, &ndecls, n);
+ break;
+ case Nfunc:
+ rdtype(fd, &n->func.type);
+ n->func.scope = rdstab(fd);
+ n->func.nargs = rdint(fd);
+ n->func.args = zalloc(sizeof(Node *)*n->func.nargs);
+ n->func.scope->super = curstab();
+ pushstab(n->func.scope->super);
+ for (i = 0; i < n->func.nargs; i++)
+ n->func.args[i] = unpickle(fd);
+ n->func.body = unpickle(fd);
+ popstab();
+ break;
+ case Nimpl:
+ n->impl.traitname = unpickle(fd);
+ i = rdint(fd);
+ n->impl.trait = htget(trmap, itop(i));
+ rdtype(fd, &n->impl.type);
+ n->impl.ndecls = rdint(fd);
+ n->impl.decls = zalloc(sizeof(Node *)*n->impl.ndecls);
+ for (i = 0; i < n->impl.ndecls; i++)
+ n->impl.decls[i] = rdsym(fd, n->impl.trait);
+ break;
+ case Nnone:
+ die("Nnone should not be seen as node type!");
+ break;
+ }
+ return n;
+}
+
+static Stab *findstab(Stab *st, char *pkg)
+{
+ Node *n;
+ Stab *s;
+
+ if (!pkg) {
+ if (!st->name)
+ return st;
+ else
+ return NULL;
+ }
+
+ n = mkname(-1, pkg);
+ if (getns(st, n)) {
+ s = getns(st, n);
+ } else {
+ s = mkstab();
+ s->name = n;
+ putns(st, s);
+ }
+ return s;
+}
+
+static void fixmappings(Stab *st)
+{
+ size_t i;
+ Type *t, *old;
+
+ /*
+ * merge duplicate definitions.
+ * This allows us to compare named types by id, instead
+ * of doing a deep walk through the type. This ability is
+ * depended on when we do type inference.
+ */
+ for (i = 0; i < ntypefixdest; i++) {
+ t = htget(tidmap, itop(typefixid[i]));
+ if (!t)
+ die("Unable to find type for id %zd\n", i);
+ if (t->type == Tyname && !t->issynth) {
+ old = htget(tydedup, t->name);
+ if (old != t)
+ if (t != old)
+ t = old;
+ }
+ *typefixdest[i] = t;
+ if (!*typefixdest[i])
+ die("Couldn't find type %zd\n", typefixid[i]);
+ }
+ /* check for duplicate type definitions */
+ for (i = 0; i < ntypefixdest; i++) {
+ t = htget(tidmap, itop(typefixid[i]));
+ if (t->type != Tyname || t->issynth)
+ continue;
+ old = htget(tydedup, t->name);
+ if (old && !tyeq(t, old))
+ fatal(-1, "Duplicate definition of type %s", tystr(old));
+ }
+ lfree(&typefixdest, &ntypefixdest);
+ lfree(&typefixid, &ntypefixid);
+}
+
+/* Usefile format:
+ * U<pkgname>
+ * T<pickled-type>
+ * R<picled-trait>
+ * I<pickled-impl>
+ * D<picled-decl>
+ * G<pickled-decl><pickled-initializer>
+ */
+int loaduse(FILE *f, Stab *st)
+{
+ intptr_t tid;
+ size_t i;
+ char *pkg;
+ Node *dcl, *impl;
+ Stab *s;
+ Type *ty;
+ Trait *tr;
+ char *lib;
+ int c;
+
+ pushstab(file->file.globls);
+ if (!tydedup)
+ tydedup = mkht(namehash, nameeq);
+ if (fgetc(f) != 'U')
+ return 0;
+ pkg = rdstr(f);
+ /* if the package names match up, or the usefile has no declared
+ * package, then we simply add to the current stab. Otherwise,
+ * we add a new stab under the current one */
+ if (st->name) {
+ if (pkg && !strcmp(pkg, namestr(st->name))) {
+ s = st;
+ } else {
+ s = findstab(st, pkg);
+ }
+ } else {
+ if (pkg) {
+ s = findstab(st, pkg);
+ } else {
+ s = st;
+ }
+ }
+ tidmap = mkht(ptrhash, ptreq);
+ trmap = mkht(ptrhash, ptreq);
+ /* builtin traits */
+ for (i = 0; i < Ntraits; i++)
+ htput(trmap, itop(i), traittab[i]);
+ while ((c = fgetc(f)) != EOF) {
+ switch(c) {
+ case 'L':
+ lib = rdstr(f);
+ for (i = 0; i < file->file.nlibdeps; i++)
+ if (!strcmp(file->file.libdeps[i], lib))
+ /* break out of both loop and switch */
+ goto foundlib;
+ lappend(&file->file.libdeps, &file->file.nlibdeps, lib);
+foundlib:
+ break;
+ case 'G':
+ case 'D':
+ dcl = rdsym(f, NULL);
+ putdcl(s, dcl);
+ break;
+ case 'R':
+ tr = traitunpickle(f);
+ puttrait(s, tr->name, tr);
+ for (i = 0; i < tr->nfuncs; i++)
+ putdcl(s, tr->funcs[i]);
+ break;
+ case 'T':
+ tid = rdint(f);
+ ty = tyunpickle(f);
+ htput(tidmap, itop(tid), ty);
+ /* fix up types */
+ if (ty->type == Tyname) {
+ if (ty->issynth)
+ break;
+ if (!gettype(st, ty->name) && !ty->ishidden)
+ puttype(s, ty->name, ty);
+ if (!hthas(tydedup, ty->name))
+ htput(tydedup, ty->name, ty);
+ } else if (ty->type == Tyunion) {
+ for (i = 0; i < ty->nmemb; i++)
+ if (!getucon(s, ty->udecls[i]->name) && !ty->udecls[i]->synth)
+ putucon(s, ty->udecls[i]);
+ }
+ break;
+ case 'I':
+ impl = unpickle(f);
+ putimpl(s, impl);
+ /* specialized declarations always go into the global stab */
+ for (i = 0; i < impl->impl.ndecls; i++)
+ putdcl(file->file.globls, impl->impl.decls[i]);
+ break;
+ case EOF:
+ break;
+ }
+ }
+ fixmappings(s);
+ htfree(tidmap);
+ popstab();
+ return 1;
+}
+
+void readuse(Node *use, Stab *st)
+{
+ size_t i;
+ FILE *fd;
+ char *p, *q;
+
+ /* local (quoted) uses are always relative to the cwd */
+ fd = NULL;
+ if (use->use.islocal) {
+ fd = fopen(use->use.name, "r");
+ /* nonlocal (barename) uses are always searched on the include path */
+ } else {
+ for (i = 0; i < nincpaths; i++) {
+ p = strjoin(incpaths[i], "/");
+ q = strjoin(p, use->use.name);
+ fd = fopen(q, "r");
+ if (fd) {
+ free(p);
+ free(q);
+ break;
+ }
+ }
+ }
+ if (!fd)
+ fatal(use->line, "Could not open %s", use->use.name);
+
+ if (!loaduse(fd, st))
+ die("Could not load usefile %s", use->use.name);
+}
+
+/* Usefile format:
+ * U<pkgname>
+ * T<pickled-type>
+ * D<picled-decl>
+ * G<pickled-decl><pickled-initializer>
+ * Z
+ */
+void writeuse(FILE *f, Node *file)
+{
+ Stab *st;
+ void **k;
+ Node *s, *u;
+ size_t i, n;
+
+ assert(file->type == Nfile);
+ st = file->file.exports;
+
+ /* usefile name */
+ wrbyte(f, 'U');
+ if (st->name)
+ wrstr(f, namestr(st->name));
+ else
+ wrstr(f, NULL);
+
+ /* library deps */
+ for (i = 0; i < file->file.nuses; i++) {
+ u = file->file.uses[i];
+ if (!u->use.islocal) {
+ wrbyte(f, 'L');
+ wrstr(f, u->use.name);
+ }
+ }
+ for (i = 0; i < file->file.nlibdeps; i++) {
+ wrbyte(f, 'L');
+ wrstr(f, file->file.libdeps[i]);
+ }
+
+ for (i = 0; i < ntypes; i++) {
+ if (types[i]->vis == Visexport || types[i]->vis == Vishidden) {
+ wrbyte(f, 'T');
+ wrint(f, types[i]->tid);
+ typickle(f, types[i]);
+ }
+ }
+
+ for (i = 0; i < ntraittab; i++) {
+ if (traittab[i]->vis == Visexport || traittab[i]->vis == Vishidden) {
+ wrbyte(f, 'R');
+ traitpickle(f, traittab[i]);
+ }
+ }
+
+ for (i = 0; i < nexportimpls; i++) {
+ /* merging during inference should remove all protos */
+ assert(!exportimpls[i]->impl.isproto);
+ if (exportimpls[i]->impl.vis == Visexport || exportimpls[i]->impl.vis == Vishidden) {
+ wrbyte(f, 'I');
+ pickle(f, exportimpls[i]);
+ }
+ }
+
+ k = htkeys(st->dcl, &n);
+ for (i = 0; i < n; i++) {
+ s = getdcl(st, k[i]);
+ /* trait functions get written out with their traits */
+ if (s->decl.trait)
+ continue;
+ if (s && s->decl.isgeneric)
+ wrbyte(f, 'G');
+ else
+ wrbyte(f, 'D');
+ wrsym(f, s);
+ }
+ free(k);
+}
--- /dev/null
+++ b/parse/util.c
@@ -1,0 +1,385 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.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 "parse.h"
+
+void *zalloc(size_t sz)
+{
+ void *mem;
+
+ mem = calloc(1, sz);
+ if (!mem && sz)
+ die("Out of memory");
+ return mem;
+}
+
+
+void *xalloc(size_t sz)
+{
+ void *mem;
+
+ mem = malloc(sz);
+ if (!mem && sz)
+ die("Out of memory");
+ return mem;
+}
+
+void *zrealloc(void *mem, size_t oldsz, size_t sz)
+{
+ char *p;
+
+ p = xrealloc(mem, sz);
+ if (sz > oldsz)
+ bzero(&p[oldsz], sz - oldsz);
+ return p;
+}
+
+void *xrealloc(void *mem, size_t sz)
+{
+ mem = realloc(mem, sz);
+ if (!mem && sz)
+ die("Out of memory");
+ return mem;
+}
+
+void die(char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ vfprintf(stderr, msg, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ abort();
+}
+
+void fatal(int line, char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ fprintf(stdout, "%s:%d: ", file->file.name, line);
+ vfprintf(stdout, msg, ap);
+ fprintf(stdout, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+/* Some systems don't have strndup. */
+char *strdupn(char *s, size_t len)
+{
+ char *ret;
+
+ ret = xalloc(len + 1);
+ memcpy(ret, s, len);
+ ret[len] = '\0';
+ return ret;
+}
+
+char *strjoin(char *u, char *v)
+{
+ size_t n;
+ char *s;
+
+ n = strlen(u) + strlen(v) + 1;
+ s = xalloc(n);
+ snprintf(s, n + 1, "%s%s", u, v);
+ return s;
+}
+
+void *memdup(void *mem, size_t len)
+{
+ void *ret;
+
+ ret = xalloc(len);
+ return memcpy(ret, mem, len);
+}
+
+void lappend(void *l, size_t *len, void *n)
+{
+ void ***pl;
+
+ pl = l;
+ *pl = xrealloc(*pl, (*len + 1)*sizeof(void*));
+ (*pl)[*len] = n;
+ (*len)++;
+}
+
+void *lpop(void *l, size_t *len)
+{
+ void ***pl;
+ void *v;
+
+ pl = l;
+ (*len)--;
+ v = (*pl)[*len];
+ *pl = xrealloc(*pl, *len * sizeof(void*));
+ return v;
+}
+
+void linsert(void *p, size_t *len, size_t idx, void *v)
+{
+ void ***pl, **l;
+
+ pl = p;
+ *pl = xrealloc(*pl, (*len + 1)*sizeof(void*));
+ l = *pl;
+
+ memmove(&l[idx + 1], &l[idx], (*len - idx)*sizeof(void*));
+ l[idx] = v;
+ (*len)++;
+}
+
+void ldel(void *p, size_t *len, size_t idx)
+{
+ void ***pl, **l;
+
+ assert(p != NULL);
+ assert(idx < *len);
+ pl = p;
+ l = *pl;
+ memmove(&l[idx], &l[idx + 1], (*len - idx - 1)*sizeof(void*));
+ (*len)--;
+ *pl = xrealloc(l, *len * sizeof(void*));
+}
+
+
+void lfree(void *l, size_t *len)
+{
+ void ***pl;
+
+ assert(l != NULL);
+ pl = l;
+ free(*pl);
+ *pl = NULL;
+ *len = 0;
+}
+
+void be64(vlong v, byte buf[8])
+{
+ buf[0] = (v >> 56) & 0xff;
+ buf[1] = (v >> 48) & 0xff;
+ buf[2] = (v >> 40) & 0xff;
+ buf[3] = (v >> 32) & 0xff;
+ buf[4] = (v >> 24) & 0xff;
+ buf[5] = (v >> 16) & 0xff;
+ buf[6] = (v >> 8) & 0xff;
+ buf[7] = (v >> 0) & 0xff;
+}
+
+vlong host64(byte buf[8])
+{
+ vlong v = 0;
+
+ v |= ((vlong)buf[0] << 56LL);
+ v |= ((vlong)buf[1] << 48LL);
+ v |= ((vlong)buf[2] << 40LL);
+ v |= ((vlong)buf[3] << 32LL);
+ v |= ((vlong)buf[4] << 24LL);
+ v |= ((vlong)buf[5] << 16LL);
+ v |= ((vlong)buf[6] << 8LL);
+ v |= ((vlong)buf[7] << 0LL);
+ return v;
+}
+
+void be32(long v, byte buf[4])
+{
+ buf[0] = (v >> 24) & 0xff;
+ buf[1] = (v >> 16) & 0xff;
+ buf[2] = (v >> 8) & 0xff;
+ buf[3] = (v >> 0) & 0xff;
+}
+
+long host32(byte buf[4])
+{
+ int32_t v = 0;
+ v |= ((long)buf[0] << 24);
+ v |= ((long)buf[1] << 16);
+ v |= ((long)buf[2] << 8);
+ v |= ((long)buf[3] << 0);
+ return v;
+}
+
+void wrbuf(FILE *fd, void *buf, size_t sz)
+{
+ size_t n;
+
+ n = 0;
+ while (n < sz) {
+ n += fwrite(buf + n, 1, sz - n, fd);
+ if (feof(fd))
+ die("Unexpected EOF");
+ if (ferror(fd))
+ die("Error writing");
+ }
+}
+
+void rdbuf(FILE *fd, void *buf, size_t sz)
+{
+ size_t n;
+
+ n = sz;
+ while (n > 0) {
+ n -= fread(buf, 1, n, fd);
+ if (feof(fd))
+ die("Unexpected EOF");
+ if (ferror(fd))
+ die("Error writing");
+ }
+}
+
+void wrbyte(FILE *fd, char val)
+{
+ if (fputc(val, fd) == EOF)
+ die("Unexpected EOF");
+}
+
+char rdbyte(FILE *fd)
+{
+ int c;
+ c = fgetc(fd);
+ if (c == EOF)
+ die("Unexpected EOF");
+ return c;
+}
+
+void wrint(FILE *fd, long val)
+{
+ byte buf[4];
+ be32(val, buf);
+ wrbuf(fd, buf, 4);
+}
+
+long rdint(FILE *fd)
+{
+ byte buf[4];
+ rdbuf(fd, buf, 4);
+ return host32(buf);
+}
+
+void wrstr(FILE *fd, char *val)
+{
+ size_t len;
+
+ if (!val) {
+ wrint(fd, -1);
+ } else {
+ wrint(fd, strlen(val));
+ len = strlen(val);
+ wrbuf(fd, val, len);
+ }
+}
+
+char *rdstr(FILE *fd)
+{
+ ssize_t len;
+ char *s;
+
+ len = rdint(fd);
+ if (len == -1) {
+ return NULL;
+ } else {
+ s = xalloc(len + 1);
+ rdbuf(fd, s, len);
+ s[len] = '\0';
+ return s;
+ }
+}
+
+void wrflt(FILE *fd, double val)
+{
+ byte buf[8];
+ /* Assumption: We have 'val' in 64 bit IEEE format */
+ union {
+ uvlong ival;
+ double fval;
+ } u;
+
+ u.fval = val;
+ be64(u.ival, buf);
+ wrbuf(fd, buf, 8);
+}
+
+double rdflt(FILE *fd)
+{
+ byte buf[8];
+ union {
+ uvlong ival;
+ double fval;
+ } u;
+
+ if (fread(buf, 8, 1, fd) < 8)
+ die("Unexpected EOF");
+ u.ival = host64(buf);
+ return u.fval;
+}
+
+void wrbool(FILE *fd, int val)
+{
+ wrbyte(fd, val);
+}
+
+int rdbool(FILE *fd)
+{
+ return rdbyte(fd);
+}
+
+char *swapsuffix(char *buf, size_t sz, char *s, char *suf, char *swap)
+{
+ size_t slen, suflen, swaplen;
+
+ slen = strlen(s);
+ suflen = strlen(suf);
+ swaplen = strlen(swap);
+
+ if (slen < suflen)
+ return NULL;
+ if (slen + swaplen >= sz)
+ die("swapsuffix: buf too small");
+
+ buf[0] = '\0';
+ /* if we have matching suffixes */
+ if (suflen < slen && !strcmp(suf, &s[slen - suflen])) {
+ strncat(buf, s, slen - suflen);
+ strncat(buf, swap, suflen);
+ } else {
+ snprintf(buf, sz, "%s%s", s, swap);
+ }
+
+ return buf;
+}
+
+size_t max(size_t a, size_t b)
+{
+ if (a > b)
+ return a;
+ else
+ return b;
+}
+
+size_t min(size_t a, size_t b)
+{
+ if (a < b)
+ return a;
+ else
+ return b;
+}
+
+size_t align(size_t sz, size_t a)
+{
+ /* align to 0 just returns sz */
+ if (a == 0)
+ return sz;
+ return (sz + a - 1) & ~(a - 1);
+}
+
--- /dev/null
+++ b/test/Makefile
@@ -1,0 +1,19 @@
+# don't build anything for 'all'
+all:
+ $(MAKE) -C ..
+
+check:
+ ./runtest.sh
+
+.PHONY: %
+%:
+ ./runtest.sh $@
+
+.PHONY: clean
+clean:
+ @for i in `awk '/^[A-Z]/{print $$2}' tests`; do \
+ echo rm -f $$i; \
+ rm -f $$i; \
+ done
+
+install:
--- /dev/null
+++ b/test/add.myr
@@ -1,0 +1,12 @@
+use std
+/* should exit with status 53 */
+const main = {
+ var a
+ var b
+ var c
+
+ a = 42
+ b = 11
+ c = 0_0
+ std.exit(a + b + c)
+}
--- /dev/null
+++ b/test/align.myr
@@ -1,0 +1,70 @@
+use std
+
+/* size should be 1 */
+type alignstruct0 = struct
+ a : byte
+;;
+
+/* size should be 16 */
+type alignstruct1 = struct
+ a : byte
+ b : byte[15]
+;;
+
+/* size should be 20 */
+type alignstruct2 = struct
+ a : int
+ b : byte[15]
+ /* 1 byte padding */
+;;
+
+/* size should be 20 */
+type alignstruct3 = struct
+ b : byte[15]
+ a : int
+;;
+
+/* size should be 8 */
+type alignstruct4 = struct
+ a : byte
+ b : int[1]
+;;
+
+/* size should be 24 */
+type alignstruct5 = struct
+ a : byte
+ b : byte[:]
+;;
+
+/* size should be 8 */
+type alignstruct6 = struct
+ a : byte
+ b : byte
+ c : byte
+ d : byte
+ e : int32
+;;
+
+/* size should be 24 */
+type alignstruct7 = struct
+ a : byte
+ b : int32
+ c : byte[:]
+;;
+
+const main = {
+ std.put("size = %i\n", sizeof(alignstruct0))
+ std.put("size = %i\n", sizeof(alignstruct1))
+ std.put("size = %i\n", sizeof(alignstruct2))
+ std.put("size = %i\n", sizeof(alignstruct3))
+ std.put("size = %i\n", sizeof(alignstruct4))
+ std.put("size = %i\n", sizeof(alignstruct5))
+ std.put("size = %i\n", sizeof(alignstruct6))
+ std.put("size = %i\n", sizeof(alignstruct7))
+ /* size should be 8 */
+ std.put("size = %i\n", sizeof([int, byte, byte]))
+ /* size should be 16 */
+ std.put("size = %i\n", sizeof([int, byte, int, byte]))
+ /* size should be 12 */
+ std.put("size = %i\n", sizeof([int, int, byte, byte]))
+}
--- /dev/null
+++ b/test/arityhigh.myr
@@ -1,0 +1,9 @@
+use std
+/* should fail because we call f with too many args */
+const f = {a:int
+ -> a
+}
+
+const main = {
+ std.exit(f(1, 2, 3))
+}
--- /dev/null
+++ b/test/aritylow.myr
@@ -1,0 +1,9 @@
+use std
+/* should fail because we call f with too few args */
+const f = {a:int, b:int, c:int
+ -> a + b + c
+}
+
+const main = {
+ std.exit(f(1, 2))
+}
--- /dev/null
+++ b/test/array.myr
@@ -1,0 +1,9 @@
+use std
+/* tests reading and writing to arrays. should exit with 7 */
+const main = {
+ var a : int[3]
+ a[0] = 3
+ a[1] = 4
+ a[2] = a[0] + a[1]
+ std.exit(a[2])
+}
--- /dev/null
+++ b/test/arrayaddr.myr
@@ -1,0 +1,11 @@
+use std
+/* tests taking the address of array elements. should exit with 42. */
+const main = {
+ var v : int[3]
+ var p
+
+ v[1] = 42
+ p = &v[1]
+ std.exit(p#)
+}
+
--- /dev/null
+++ b/test/arraylen.myr
@@ -1,0 +1,7 @@
+use std
+/* checks that array lengths work. should exit with 12. */
+const main = {
+ var a : int[12]
+
+ std.exit(a.len)
+}
--- /dev/null
+++ b/test/arraylit-ni.myr
@@ -1,0 +1,6 @@
+use std
+/* checks that we can create arrays without indexed initializers. exits with 2. */
+const main = {
+ var a = [1, 3, 2]
+ std.exit(a[2])
+}
--- /dev/null
+++ b/test/arraylit.myr
@@ -1,0 +1,7 @@
+/* checks we can make indexed array literals. exits with 3. */
+const main = {
+ var a = [#0=1,
+ #2=3,
+ #1=2]
+ -> a[2]
+}
--- /dev/null
+++ b/test/badop.myr
@@ -1,0 +1,6 @@
+use std
+
+const main = {
+ var x
+ x = "a" + "b"
+}
--- /dev/null
+++ b/test/basicfloat.myr
@@ -1,0 +1,10 @@
+use std
+
+const get42 = {
+ -> 42.0
+}
+
+/* basic sanity check on floating point operations. should return 84. */
+const main = {
+ std.exit((42.0 + get42()) castto(int))
+}
--- /dev/null
+++ b/test/bigint.myr
@@ -1,0 +1,86 @@
+use std
+
+const main = {
+ var a, b, c, d, e
+ var buf : byte[1024], n
+
+ a = std.mkbigint(1234)
+ b = std.mkbigint(0x7fffffff)
+ c = std.mkbigint(7919)
+ d = std.mkbigint(113051)
+ e = std.mkbigint(11)
+
+ std.bigmul(a, b)
+ std.bigmul(a, b)
+ std.bigadd(a, c)
+ std.bigsub(a, d)
+ std.bigdiv(a, e)
+
+ std.bigfree(b)
+ std.bigfree(c)
+ std.bigfree(d)
+ std.bigfree(e)
+
+ n = std.bigfmt(buf[:], a)
+ std.put("%s\n", buf[:n])
+
+ /* smoke test */
+ match std.bigparse("1234_5678_1234_6789_6666_7777_8888")
+ | `std.Some val: a = val
+ | `std.None: std.die("Failed to parse a\n")
+ ;;
+ match std.bigparse("1234_5678_1234_6789_6666_7777")
+ | `std.Some val: b = val
+ | `std.None: std.die("Failed to parse b\n")
+ ;;
+
+ n = std.bigfmt(buf[:], a)
+ std.put("%s / ", buf[:n])
+ n = std.bigfmt(buf[:], b)
+ std.put("%s == ", buf[:n])
+
+ std.bigdiv(a, b)
+
+ n = std.bigfmt(buf[:], a)
+ std.put("%s\n", buf[:n])
+
+ /* no shifting */
+ match std.bigparse("0xffff_1234_1234_1234_1234")
+ | `std.Some val: a = val
+ | `std.None: std.die("Failed to parse a\n")
+ ;;
+ match std.bigparse("0xf010_1234_2314")
+ | `std.Some val: b = val
+ | `std.None: std.die("Failed to parse b\n")
+ ;;
+
+ n = std.bigfmt(buf[:], a)
+ std.put("%s / ", buf[:n])
+ n = std.bigfmt(buf[:], b)
+ std.put("%s == ", buf[:n])
+
+ std.bigdiv(a, b)
+
+ n = std.bigfmt(buf[:], a)
+ std.put("%s\n", buf[:n])
+
+ /* no shifting */
+ match std.bigparse("0xffff_1234_1234_1234_1234")
+ | `std.Some val: a = val
+ | `std.None: std.die("Failed to parse a\n")
+ ;;
+ match std.bigparse("0x0ff_1234_2314")
+ | `std.Some val: b = val
+ | `std.None: std.die("Failed to parse b\n")
+ ;;
+
+ n = std.bigfmt(buf[:], a)
+ std.put("%s / ", buf[:n])
+ n = std.bigfmt(buf[:], b)
+ std.put("%s == ", buf[:n])
+
+ std.bigdiv(a, b)
+
+ n = std.bigfmt(buf[:], a)
+ std.put("%s\n", buf[:n])
+}
--- /dev/null
+++ b/test/bigliteral.myr
@@ -1,0 +1,5 @@
+use std
+
+const main = {
+ std.put("%l\n", 34359738368 castto(int64))
+}
--- /dev/null
+++ b/test/bsr.myr
@@ -1,0 +1,6 @@
+use std
+/* should exit with status 5 */
+const main = {
+ var a = 42
+ std.exit(a >> 3)
+}
--- /dev/null
+++ b/test/call.myr
@@ -1,0 +1,9 @@
+use std
+/* checks that simple function calls work. should exit with 42. */
+const f = {
+ -> 21
+}
+
+const main = {
+ std.exit(f() + f())
+}
--- /dev/null
+++ b/test/callbig.myr
@@ -1,0 +1,18 @@
+use std
+/* checks that calls with large return values (ie, ones that don't fit in a
+* register) works correctly. Should exit with 42. */
+type pair = struct
+ a : int
+ b : int
+;;
+
+const f = {s
+ -> s.a + s.b
+}
+
+const main = {
+ var s : pair
+ s.a = 12
+ s.b = 30
+ std.exit(f(s))
+}
--- /dev/null
+++ b/test/catfile.myr
@@ -1,0 +1,14 @@
+/* checks that we can read a data file. */
+use std
+
+const main = {args : byte[:][:]
+ var r
+
+ r = std.slurp("data/catfile-in")
+ match r
+ | `std.Ok dat: std.write(1, dat)
+ | `std.Fail msg: std.put("Failed to read file: %s\n", msg)
+ ;;
+ -> 0
+}
+
--- /dev/null
+++ b/test/chartest.myr
@@ -1,0 +1,7 @@
+use std
+
+const main = {
+ std.assert('a' == '\u{61}', "unicode char values invalid")
+ std.assert('Σ' == '\u{03a3}', "unicode char values invalid")
+ -> 0
+}
--- /dev/null
+++ b/test/closure.myr
@@ -1,0 +1,8 @@
+/* checks that functions with environment capture work. should exit with 42. */
+const main = {
+ var a = 42
+ var f = {b
+ -> a + b
+ }
+ -> f(13)
+}
--- /dev/null
+++ b/test/compoundimpl.myr
@@ -1,0 +1,29 @@
+use std
+
+trait frobable @a =
+ frob : (val : @a -> void)
+;;
+
+impl frobable int# =
+ frob = {val
+ std.put("intptr,")
+ }
+;;
+
+impl frobable char# =
+ frob = {val
+ std.put("charptr\n")
+ }
+;;
+
+generic foo = {x : @a::frobable
+ frob(x)
+}
+
+const main = {
+ var a = 123
+ var b = 'c'
+ foo(&a)
+ foo(&b)
+}
+
--- /dev/null
+++ b/test/condiffalse.myr
@@ -1,0 +1,12 @@
+use std
+/* checks that false conditions lead to the false branch of an if statement.
+* should exit with 9. */
+const main = {
+ var x = 5, y = 7
+
+ if x == 7 && y == 5
+ std.exit(7)
+ else
+ std.exit(9)
+ ;;
+}
--- /dev/null
+++ b/test/condifrel.myr
@@ -1,0 +1,11 @@
+use std
+/* checks if relatonal operators work. should exit with 9. */
+const main = {
+ var x = 3, y = 9
+
+ if x < 5 && y > 7
+ std.exit(7)
+ else
+ std.exit(9)
+ ;;
+}
--- /dev/null
+++ b/test/condiftrue.myr
@@ -1,0 +1,11 @@
+use std
+/* checks that true complex boolean conditions work. exits with 7. */
+const main = {
+ var x = 5, y = 7
+
+ if x == 5 && y == 7
+ std.exit(7)
+ else
+ std.exit(9)
+ ;;
+}
--- /dev/null
+++ b/test/constslice.myr
@@ -1,0 +1,22 @@
+use std
+
+const slpart = array[1:3]
+const slfull = array[:]
+const slinline = [6,7,8][:]
+const array = [1,2,3,4,5]
+
+const main = {
+ /* expected output 23 */
+ for x in slpart
+ std.put("%i", x)
+ ;;
+ /* expected output 12345 */
+ for x in slfull
+ std.put("%i", x)
+ ;;
+ /* expected output 678 */
+ for x in slinline
+ std.put("%i", x)
+ ;;
+ std.put("\n")
+}
--- /dev/null
+++ b/test/data/align-expected
@@ -1,0 +1,11 @@
+size = 1
+size = 16
+size = 20
+size = 20
+size = 8
+size = 24
+size = 8
+size = 24
+size = 8
+size = 16
+size = 12
--- /dev/null
+++ b/test/data/bigint-expected
@@ -1,0 +1,4 @@
+517347321949036993306
+1234567812346789666677778888 / 123456781234678966667777 == 10000
+5192296858534810493479828944327220 / 1208925819597106013545236 == 4294967296
+5192296858534810493479828944327220 / 75557863709417659441940 == 68719476751
--- /dev/null
+++ b/test/data/catfile-expected
@@ -1,0 +1,1 @@
+Hello-世界
--- /dev/null
+++ b/test/data/catfile-in
@@ -1,0 +1,1 @@
+Hello-世界
--- /dev/null
+++ b/test/data/matchargstr-expected
@@ -1,0 +1,1 @@
+Correct `Str "asdf"!
--- /dev/null
+++ b/test/data/stdslcp-expected
@@ -1,0 +1,2 @@
+3 4 5 4 5
+6 7 6 7 8
--- /dev/null
+++ b/test/data/stdsort-expected
@@ -1,0 +1,30 @@
+0
+2
+1
+3
+4
+5
+6
+7
+8
+9
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+C
+Cc
+a
+aa
+b
+cC
+d
+f
+fuckit
+go
--- /dev/null
+++ b/test/data/stdtry-expected
@@ -1,0 +1,1 @@
+expected `Some @a, got `None
--- /dev/null
+++ b/test/data/strfind-expected
@@ -1,0 +1,10 @@
+No match
+No match
+No match
+Found 0
+Found 0
+No match
+Found 1
+Found 2
+No match
+No match
--- /dev/null
+++ b/test/data/strjoin-expected
@@ -1,0 +1,3 @@
+foo;bar
+xyzw
+x:y:z:w
--- /dev/null
+++ b/test/data/strsplit-expected
@@ -1,0 +1,13 @@
+"a"
+"b"
+"c"
+"d"
+"a"
+""
+"b"
+"c"
+""
+"d"
+"a--b"
+"b--c"
+"c-d--d"
--- /dev/null
+++ b/test/data/strstrip-expected
@@ -1,0 +1,15 @@
+"abc"
+"abc "
+" abc"
+--
+"世界"
+"世界 "
+" 世界"
+--
+""
+""
+""
+--
+""
+""
+""
--- /dev/null
+++ b/test/data/strtab-expected
@@ -1,0 +1,4 @@
+0: foo
+1: bar
+2: baz
+3: quux
--- /dev/null
+++ b/test/declmismatch.myr
@@ -1,0 +1,11 @@
+use std
+/*
+should fail to compile with a type error.
+char is incompatible with int.
+*/
+const main = {
+ var a : int
+ var b : char
+
+ a = b
+}
--- /dev/null
+++ b/test/derefassign.myr
@@ -1,0 +1,10 @@
+use std
+/* should assign to v through pointer p, exiting with 123 */
+const main = {
+ var p
+ var v
+
+ p = &v
+ p# = 123
+ std.exit(v)
+}
--- /dev/null
+++ b/test/div.myr
@@ -1,0 +1,10 @@
+use std
+/* should exit with status 42 */
+const main = {
+ var a
+ var b
+
+ a = 127
+ b = 3
+ std.exit(a / b)
+}
--- /dev/null
+++ b/test/doublecall.myr
@@ -1,0 +1,13 @@
+use std
+
+const main = {
+ std.put("%i,%w\n", a(), b())
+}
+
+const a = {
+ -> 42
+}
+
+const b = {
+ -> 33 castto(int16)
+}
--- /dev/null
+++ b/test/emptytrait.myr
@@ -1,0 +1,14 @@
+use std
+
+trait fooable @a =
+;;
+impl fooable int
+
+generic foo = {x : @a::fooable
+ -> x
+}
+
+const main = {
+ std.exit(foo(123))
+}
+
--- /dev/null
+++ b/test/encodechar.myr
@@ -1,0 +1,23 @@
+/* checks that we can decode and encode characters from a utf8 string. */
+use std
+
+const main = {args : byte[:][:]
+ chartypes()
+}
+
+const chartypes = {
+ var s
+ var c
+ var foo
+ var buf : byte[32]
+
+ s = "1世界äa\n"
+ while s.len != 0
+ (c, s) = std.striter(s)
+ foo = c
+ if !std.encode(buf[:std.charlen(c)], c)
+ std.write(1, "couldn't encode\n")
+ ;;
+ std.write(1, buf[:std.charlen(c)])
+ ;;
+}
--- /dev/null
+++ b/test/exportcycle.myr
@@ -1,0 +1,12 @@
+use std
+/* This test checks that cyclic types will be output
+ * by muse. */
+pkg =
+ type list = struct
+ next : list#
+ val : int
+ ;;
+;;
+
+const main = {
+}
--- /dev/null
+++ b/test/exportmain.myr
@@ -1,0 +1,14 @@
+use std
+
+pkg =
+ const foo : (val:int -> int)
+;;
+
+
+const foo = {val
+ -> val
+}
+
+const main = {
+ std.exit(foo(42))
+}
--- /dev/null
+++ b/test/exporttrait.myr
@@ -1,0 +1,20 @@
+use std
+
+pkg =
+ trait t @a
+ impl t int
+;;
+
+trait t @a =
+ frob : (v : @a -> @a)
+;;
+
+impl t int =
+ frob = {v
+ -> v*2
+ }
+;;
+
+/* shut up the linker: we just want to compile this. */
+const main = {
+}
--- /dev/null
+++ b/test/fib.myr
@@ -1,0 +1,16 @@
+use std
+/* checks if recursive functions work. should return 21. */
+const fib = {n
+ if n <= 0
+ -> 0
+ elif n == 1
+ -> 1
+ else
+ -> fib(n - 1) + fib(n - 2)
+ ;;
+}
+
+const main = {
+ std.exit(fib(8))
+}
+
--- /dev/null
+++ b/test/generic-in-const.myr
@@ -1,0 +1,8 @@
+use std
+/*
+should fail to compile because generic types
+are only allowed in generic declarations.
+*/
+const foo = {v : @a
+ -> v
+}
--- /dev/null
+++ b/test/generic.myr
@@ -1,0 +1,11 @@
+use std
+/* checks that simple generics are specialized correctly. exits with 42. */
+generic id = {a:@a
+ -> a
+}
+
+const main = {
+ id("adsf")
+ std.exit(id(42))
+}
+
--- /dev/null
+++ b/test/genericcall.myr
@@ -1,0 +1,15 @@
+use std
+/* checks that generics can call non-generics. exits with 42. */
+const f = {
+ -> 42
+}
+
+generic id = {a:@a
+ -> f()
+}
+
+const main = {
+ id("adsf")
+ std.exit(id(42))
+}
+
--- /dev/null
+++ b/test/genericmatch.myr
@@ -1,0 +1,13 @@
+use std
+
+type t(@a) = union
+ `Foo @a
+ `Bar
+;;
+
+const main = {
+ match `Foo 123
+ | `Foo a: std.exit(0xf)
+ | `Bar: std.exit(0x0)
+ ;;
+}
--- /dev/null
+++ b/test/genericrec.myr
@@ -1,0 +1,11 @@
+use std
+/* test that generic types can be recursive, as long as they're not self
+ * including. This just needs to compile and exit with 0. */
+type list(@t) = struct
+ val : @t
+ next : list(@t)#
+;;
+
+const main = {
+ var v : list(int)
+}
--- /dev/null
+++ b/test/genericret.myr
@@ -1,0 +1,17 @@
+use std
+
+type t(@a) = union
+ `Val @a
+ `None
+;;
+
+const f = {-> t(int)
+ -> `None
+}
+
+const main = {
+ match f()
+ | `None: std.exit(42)
+ ;;
+ std.exit(0)
+}
--- /dev/null
+++ b/test/generictype.myr
@@ -1,0 +1,18 @@
+use std
+
+/* checks that parameterized types work. exits with 0. */
+type option(@a) = union
+ `Some @a::(integral,numeric)
+ `None
+;;
+
+const main = {
+ var v
+
+ v = `Some 123
+ match v
+ | `None: std.exit(1)
+ | `Some 123: std.exit(0)
+ ;;
+ std.exit(60)
+}
--- /dev/null
+++ b/test/genericval.myr
@@ -1,0 +1,8 @@
+use std
+
+generic Foo : @a::(integral,numeric) = 42
+
+const main = {
+ std.exit(Foo)
+}
+
--- /dev/null
+++ b/test/global-arrayvar.myr
@@ -1,0 +1,10 @@
+use std
+/* tests that global arrays work as expected, and are zero-initialized by
+* default. should exit with 7 */
+var a : int[10]
+
+const main = {
+ a[0] = 3
+ a[1] = 4
+ std.exit(a[0] + a[1] + a[2] + a[3])
+}
--- /dev/null
+++ b/test/gsizeof.myr
@@ -1,0 +1,9 @@
+use std
+/* checks that sizeof works on generics. exits with 5. */
+generic sz = {a:@a
+ -> sizeof(@a)
+}
+
+const main = {
+ std.exit(sz(123) + sz("asdf"[0]))
+}
--- /dev/null
+++ b/test/helloworld.myr
@@ -1,0 +1,7 @@
+/* checks that this program prints Hello-世界\n */
+use std
+
+const main = {args : byte[:][:]
+ std.write(1, "Hello-世界\n")
+}
+
--- /dev/null
+++ b/test/import-type.myr
@@ -1,0 +1,7 @@
+use std
+
+const main = {
+ var x : std.size
+ var y : std.off
+ var z : std.statbuf
+}
--- /dev/null
+++ b/test/incret.myr
@@ -1,0 +1,11 @@
+use std
+
+var i = 0
+const f = {
+ -> i++
+}
+
+const main = {
+ f()
+ std.exit(i)
+}
--- /dev/null
+++ b/test/infer-named.myr
@@ -1,0 +1,21 @@
+use std
+
+type u = union
+ `Foo
+ `Bar int
+;;
+
+const f = {v : int -> u
+ -> `Bar v
+}
+
+const main = {
+ var v
+
+ v = f(99)
+ match v
+ | `Foo: std.exit(1)
+ | `Bar x: std.exit(x)
+ ;;
+ std.exit(2)
+}
--- /dev/null
+++ b/test/infermismatch.myr
@@ -1,0 +1,13 @@
+use std
+/*
+should fail to compile after infering that a is a float, b is a char, and
+the types are incompatible.
+*/
+const main = {
+ var a
+ var b
+
+ a = 1.0
+ b = 'a'
+ a = b
+}
--- /dev/null
+++ b/test/livearraylit.myr
@@ -1,0 +1,12 @@
+use std
+
+const main = {
+ var v
+
+ v = [foo(), 42, 123]
+ std.exit(v[0])
+}
+
+const foo = {
+ -> 21
+}
--- /dev/null
+++ b/test/livestructlit.myr
@@ -1,0 +1,20 @@
+use std
+
+/* checks that we can create struct literals with named initializers.
+ exits with 42. */
+type t = struct
+ a : int
+ b : byte
+ c : byte[:]
+;;
+
+const main = {
+ var v : t
+
+ v = [.a=foo(), .b=42, .c="foo"]
+ std.exit(v.a)
+}
+
+const foo = {
+ -> 21
+}
--- /dev/null
+++ b/test/local-labels.myr
@@ -1,0 +1,15 @@
+use std
+
+const main = {
+ goto foo
+ std.exit(123)
+:foo
+ std.exit(bar())
+}
+
+const bar = {
+ goto foo
+ -> 42
+:foo
+ -> 10
+}
--- /dev/null
+++ b/test/log-and.myr
@@ -1,0 +1,5 @@
+use std
+/* checks that evaluating a logical and to a bool works. should return 0. */
+const main = {
+ std.exit((0 && 1) castto(int))
+}
--- /dev/null
+++ b/test/log-or.myr
@@ -1,0 +1,5 @@
+use std
+/* checks that evaluating a logical or works. exits with 1. */
+const main = {
+ std.exit((0 || 1) castto(int))
+}
--- /dev/null
+++ b/test/loop.myr
@@ -1,0 +1,24 @@
+use std
+/* checks that loops work. exits with 45. */
+const main = {
+ var i
+ var n
+
+ n = 0
+ for i = 0; i < 5; ++i
+ std.put("%i", i)
+ ;;
+ for i = 0; i < 5; ++i
+ if i > 3
+ break
+ ;;
+ std.put("%i", i)
+ ;;
+ for i = 0; i < 10; ++i
+ if i < 6
+ continue
+ ;;
+ std.put("%i", i)
+ ;;
+ std.put("\n")
+}
--- /dev/null
+++ b/test/main.myr
@@ -1,0 +1,3 @@
+use std
+/* should exit with status 0 */
+const main = {;}
--- /dev/null
+++ b/test/match-badtypes.myr
@@ -1,0 +1,14 @@
+use std
+/*
+should fail to compile because
+all types matched over should be
+compatible. Strings are not compatible
+with integers.
+*/
+const foo = {
+ match 123
+ |"asdf": 123
+ |234567: 888
+ ;;
+ std.exit(42)
+}
--- /dev/null
+++ b/test/matchargstr.myr
@@ -1,0 +1,22 @@
+use std
+/* checks pattern matching on unions with arguments.
+exits with 42. */
+type u = union
+ `Int int
+ `Str byte[:]
+ `Nil
+;;
+
+const main = {
+ var v
+
+ v = `Str "asdf"
+ match v
+ | `Int 127: std.fatal(1, "wrong match `Int 127\n")
+ | `Str "foo": std.fatal(1, "Wrong match `Str \"foo\"\n")
+ | `Str "fsda": std.fatal(1, "Wrong match `Str \"fsda\"\n")
+ | `Str "asdf": std.put("Correct `Str \"asdf\"!\n")
+ | `Nil: std.fatal(1, "Wrong match `Str \"fsda\"\n")
+ ;;
+}
+
--- /dev/null
+++ b/test/matchargunion.myr
@@ -1,0 +1,21 @@
+use std
+/* checks pattern matching on unions with arguments.
+exits with 42. */
+type u = union
+ `Int int
+ `Chr char
+ `Nil
+;;
+
+const main = {
+ var v
+
+ v = `Int 123
+ match v
+ | `Int 127: std.exit(42)
+ | `Int 123: std.exit(69)
+ | `Chr 'a': std.exit(4)
+ | `Nil: std.exit(6)
+ ;;
+}
+
--- /dev/null
+++ b/test/matcharray.myr
@@ -1,0 +1,12 @@
+use std
+
+const main = {
+ var v = [2, 40, 10]
+
+ match v
+ | [x, y, 10]:
+ std.exit(x + y)
+ | _: std.die("Wat")
+ ;;
+ std.exit(0)
+}
--- /dev/null
+++ b/test/matchbind.myr
@@ -1,0 +1,21 @@
+use std
+/* checks that we can bind values in pattern matches.
+exits with 11. */
+type u = union
+ `Int int
+ `Chr char
+ `Nil
+;;
+
+const main = {
+ var v
+
+ v = `Int 8
+ match v
+ | `Int 127: std.exit(42)
+ | `Int x: std.exit(x)
+ | `Chr 'a': std.exit(4)
+ | `Nil: std.exit(6)
+ ;;
+}
+
--- /dev/null
+++ b/test/matchconst.myr
@@ -1,0 +1,18 @@
+use std
+/* checks that matching works when comparing against constants,
+instead of just literals. exits with 88. */
+/* some misc constants */
+const Ca = 123
+const Cb = 8
+const Cc = 42
+
+const main = {
+ var v
+
+ v = 8
+ match v
+ | Ca: std.exit(123)
+ | Cb: std.exit(88)
+ | Cc: std.exit(42)
+ ;;
+}
--- /dev/null
+++ b/test/matchint.myr
@@ -1,0 +1,15 @@
+use std
+/* checks that matching integers works. exits with 84. */
+const main = {
+ var v
+
+ v = 12
+ match 12
+ | 1: std.exit(42)
+ | 2: std.exit(81)
+ | 3: std.exit(123)
+ | 4: std.exit(99)
+ | 12: std.exit(84)
+ | 6: std.exit(18)
+ ;;
+}
--- /dev/null
+++ b/test/matchmixed.myr
@@ -1,0 +1,14 @@
+use std
+
+type u = union
+ `A
+ `B
+;;
+
+const main = {
+ match "asdf"
+ | `A: std.put("Got a\n")
+ | `B: std.put("Got b\n")
+ ;;
+ std.exit(42)
+}
--- /dev/null
+++ b/test/matchstruct.myr
@@ -1,0 +1,22 @@
+use std
+
+type t = struct
+ v1 : int
+ v2 : int
+ v3 : int
+;;
+
+const main = {
+ var v : t
+
+ v.v1 = 2
+ v.v2 = 40
+ v.v3 = 10
+ match v
+ | [.v1 = x, .v2 = y, .v3 = 10]:
+ std.exit(x + y)
+ | _:
+ std.die("Wat")
+ ;;
+ std.exit(0)
+}
--- /dev/null
+++ b/test/matchtup.myr
@@ -1,0 +1,11 @@
+use std
+
+const main = {
+ var v = (1, 2)
+
+ match v
+ | (1, x): std.exit(40 + x)
+ | _: std.die("Wat")
+ ;;
+ std.exit(0)
+}
--- /dev/null
+++ b/test/matchunion.myr
@@ -1,0 +1,21 @@
+use std
+/* checks that union matching works, at least on the key.
+exits with 84. */
+type u = union
+ `Foo
+ `Bar
+ `Baz
+ `Quux
+;;
+
+const main = {
+ var v
+
+ v = `Foo
+ match v
+ | `Bar: std.exit(42)
+ | `Baz: std.exit(81)
+ | `Foo: std.exit(84)
+ | `Quux: std.exit(123)
+ ;;
+}
--- /dev/null
+++ b/test/matchunion_sl.myr
@@ -1,0 +1,20 @@
+use std
+/* checks pattern matching on unions with arguments.
+exits with 42. */
+type u = union
+ `Int int
+ `Str byte[:]
+ `Nil
+;;
+
+const main = {
+ var v
+
+ v = `Str "foo"
+ match v
+ | `Int 127: std.exit(42)
+ | `Str s: std.put("%s\n", s)
+ | `Nil:
+ ;;
+}
+
--- /dev/null
+++ b/test/mkunion.myr
@@ -1,0 +1,13 @@
+use std
+/* checks that union creation works. exits with 0. */
+type u = union
+ `Some int
+ `None
+;;
+
+const main = {
+ var v
+
+ v = `Some 123
+ std.exit(0)
+}
--- /dev/null
+++ b/test/mod.myr
@@ -1,0 +1,7 @@
+use std
+/* should exit with status 6 */
+const main = {
+ var a = 42
+ var b = 9
+ std.exit(a % b)
+}
--- /dev/null
+++ b/test/mul.myr
@@ -1,0 +1,8 @@
+use std
+/* should exit with status 42 */
+const main = {
+ var a = 7
+ var b = 2
+ var c = 3
+ std.exit(a * b * c)
+}
--- /dev/null
+++ b/test/mul8.myr
@@ -1,0 +1,9 @@
+use std
+
+var a : int8 = 6
+const f = { -> int8
+ -> 3
+}
+const main = {
+ std.exit(((a*f()) castto(int)))
+}
--- /dev/null
+++ b/test/nestfn.myr
@@ -1,0 +1,9 @@
+use std
+/* checks that nested functions without environment capture work. should
+* exit with 42. */
+const main = {
+ const ret42 = {
+ -> 42
+ }
+ std.exit(ret42())
+}
--- /dev/null
+++ b/test/neststruct.myr
@@ -1,0 +1,21 @@
+use std
+/* tests that nested structs work. should exit with 3 */
+type s1 = struct
+ x : s2
+;;
+
+type s2 = struct
+ a : int
+ b : int
+;;
+
+const main = {
+ var s1 : s1
+ var s2 : s2
+
+ s1.x.a = 1
+ s1.x.b = 2
+ s2 = s1.x
+
+ std.exit(s2.a + s2.b)
+}
--- /dev/null
+++ b/test/nestucon.myr
@@ -1,0 +1,18 @@
+use std
+
+type t = struct
+ x : union
+ `Int int
+ `Str byte[:]
+ ;;
+;;
+
+const main = {
+ var a : t
+
+ a = [.x = `Str "asdf"]
+ match a
+ | [.x=`Str s]: std.put("%s\n", s)
+ ;;
+}
+
--- /dev/null
+++ b/test/occur.myr
@@ -1,0 +1,10 @@
+use std
+/* checks that f is not an infinite type (ie, the type
+doesn't exist within itself). If 'f' typechecked,
+it's type would be:
+
+f : (std.exit((-> (-> ... ad infinitum ...))))
+*/
+const f = {
+ std.exit(f)
+}
--- /dev/null
+++ b/test/outparam-sl.myr
@@ -1,0 +1,13 @@
+use std
+/* should assign a slice through an out param, returning exiting with 2 */
+const arr = [1,2,3,4]
+const f = {out
+ out# = arr[1:3]
+}
+
+const main = {
+ var v
+
+ f(&v)
+ std.exit(v[0])
+}
--- /dev/null
+++ b/test/outparam.myr
@@ -1,0 +1,13 @@
+use std
+/* should assign through an out pointer parameter, exiting with status 42 */
+const f = {out
+ out# = 42
+}
+
+const main = {
+ var v
+
+ v = 16
+ f(&v)
+ std.exit(v)
+}
--- /dev/null
+++ b/test/overlappingif.myr
@@ -1,0 +1,18 @@
+use std
+/* checks that if multiple if conditions are valid, only the first is
+* selected. should exit with 2. */
+const main = {
+ var v
+ var x
+
+ v = 0xff
+
+ if v & 0xff00 != 0
+ x = 1
+ elif v & 0xfff0 != 0
+ x = 2
+ elif v & 0xffff != 0
+ x = 3
+ ;;
+ std.exit(x)
+}
--- /dev/null
+++ b/test/patiter.myr
@@ -1,0 +1,13 @@
+use std
+
+const main = {
+ /* should print 1,3,5, skipping 4 */
+ for (1,x) in [(1,2),(1,3),(2,4),(1,5)]
+ std.put("%i", x)
+ ;;
+ /* should print 1, 2 skipping `None */
+ for `std.Some v in [`std.None, `std.Some 1, `std.Some 2]
+ std.put("%i", v)
+ ;;
+ std.put("\n")
+}
--- /dev/null
+++ b/test/ptrpreinc.myr
@@ -1,0 +1,12 @@
+use std
+/* should preincrement through a pointer, exiting with status 9 */
+const ppreinc = {p
+ -> ++p#
+}
+
+const main = {
+ var x = 8
+
+ std.exit(ppreinc(&x))
+}
+
--- /dev/null
+++ b/test/runtest.sh
@@ -1,0 +1,136 @@
+#!/bin/bash
+export PATH=.:$PATH
+export MC=../6/6m
+export MU=../muse/muse
+export AS=AS
+export LD=ld
+ARGS=$*
+NFAILURES=0
+NPASSES=0
+
+function use {
+ rm -f $1 $1.o $1.s $1.use
+ echo " "$MU -I ../libstd -o $1.use $1.myr && \
+ $MU $1.myr -o $1.use
+}
+
+function build {
+ rm -f $1 $1.o $1.s $1.use
+ ../myrbuild/myrbuild -b $1 -C../6/6m -M../muse/muse -I../libstd $1.myr
+}
+
+function pass {
+ PASSED="$PASSED $1"
+ NPASSED=$[$NPASSED + 1]
+}
+
+function fail {
+ echo "FAIL: $1"
+ FAILED="$FAILED $1"
+ NFAILED=$[$NFAILED + 1]
+}
+
+function expectstatus {
+ ./$1 $3
+ if [ $? -eq $2 ]; then
+ pass $1
+ return
+ else
+ fail $1
+ fi
+}
+
+function expectprint {
+ if [ "`./$1 $3`" != "$2" ]; then
+ fail $1
+ else
+ pass $1
+ fi
+}
+
+
+function expectcompare {
+ if [ x"" != x"$TMPDIR" ]; then
+ t=$TMPDIR/myrtest-$1-$RANDOM
+ else
+ t=/tmp/myrtest-$1-$RANDOM
+ fi
+ ./$1 $3 > $t
+ if cmp $t data/$1-expected; then
+ pass $1
+ else
+ fail $1
+ fi
+ rm -f $t
+}
+
+function expectfcompare {
+ ./$1 $3
+ if cmp data/$1-expected $2; then
+ pass $1
+ else
+ fail $1
+ fi
+}
+
+function shouldskip {
+ if [ -z $ARGS ]; then
+ return 1
+ fi
+
+ for i in $ARGS; do
+ if [ $i = $1 ]; then
+ return 1
+ fi
+ done
+ return 0
+}
+
+
+# Should build and run
+function B {
+ if shouldskip $1; then
+ return
+ fi
+
+ test="$1"; shift
+ type="$1"; shift
+ res="$1"; shift
+ if [ $# > 0 ]; then
+ args="$1"; shift
+ fi
+ build $test
+ case $type in
+ "E") expectstatus "$test" "$res" "$input";;
+ "P") expectprint "$test" "$res" "$input";;
+ "C") expectcompare "$test" "$res" "$input";;
+ "F") expectfcompare "$test" "$res" "$args";;
+ esac
+}
+
+# Should fail
+function F {
+ if shouldskip $1; then
+ return
+ fi
+ (build $1) > /dev/null
+ if [ $? -eq '1' ]; then
+ pass $1
+ else
+ fail $1
+ fi
+}
+
+# Should generate a usefile
+function U {
+ return
+}
+
+source tests
+
+echo "PASSED ($NPASSED): $PASSED"
+if [ -z "$NFAILED" ]; then
+ echo "SUCCESS"
+else
+ echo "FAILURES ($NFAILED): $FAILED"
+fi
--- /dev/null
+++ b/test/sizeof.myr
@@ -1,0 +1,5 @@
+use std
+/* checks that sizeof() works. exits with 4. */
+const main = {
+ std.exit(sizeof(int))
+}
--- /dev/null
+++ b/test/slalloc.myr
@@ -1,0 +1,11 @@
+/* test the allocation of slices. should return 123 */
+use std
+
+const main = {
+ var sl : int[:]
+
+ sl = std.slalloc(123)
+ sl[0] = 42
+ sl[122] = 1
+ std.exit(sl.len)
+}
--- /dev/null
+++ b/test/slgrow.myr
@@ -1,0 +1,12 @@
+/* checks that our slice grow function works. exits with 42. */
+use std
+
+const main = {
+ var sl
+
+ sl = std.slalloc(42)
+ sl[0] = 12
+ sl = std.slgrow(sl, 123)
+ sl[122] = 30
+ std.exit(sl[0] + sl[122])
+}
--- /dev/null
+++ b/test/slice.myr
@@ -1,0 +1,12 @@
+use std
+/* checks that taking slices of arrays works. should exit with 7 */
+const main = {
+ var a : int[3]
+ var s
+
+ s = a[:]
+ s[0] = 3
+ s[1] = 4
+ s[2] = s[0] + s[1] + s.len
+ std.exit(s[2])
+}
--- /dev/null
+++ b/test/slicelen.myr
@@ -1,0 +1,10 @@
+use std
+/* checks that taking incomplete slices calculates the length correctly.
+* should exit with 5. */
+const main = {
+ var a : int[8]
+ var s
+
+ s = a[1:6]
+ std.exit(s.len)
+}
--- /dev/null
+++ b/test/splitline.myr
@@ -1,0 +1,6 @@
+use std
+
+const main = {
+ std.exit(1 + \ /* ignored crap */
+ 2)
+}
--- /dev/null
+++ b/test/sqrt.myr
@@ -1,0 +1,33 @@
+use std
+
+const abs = {d
+ if d < 0.0
+ -> -d
+ else
+ -> d
+ ;;
+}
+
+const Eps = 0.00001
+const Maxiter = 20
+
+const sqrt = {x : float64
+ var val
+ var iter
+ var i;
+
+ val = 1.0;
+ for i = 0; i < Maxiter; i++
+ iter = 0.5*(val + x/val)
+ if abs(val - iter) < Eps
+ -> val;
+ ;;
+ val = iter;
+ ;;
+ -> val
+}
+
+const main = {
+ std.exit(sqrt(20.0) castto(int))
+}
+
--- /dev/null
+++ b/test/stdopt-mk.myr
@@ -1,0 +1,20 @@
+use std
+
+const f = {x
+ if x == 123
+ -> `std.Some 42
+ else
+ -> `std.None
+ ;;
+}
+
+const main = {
+ var v
+
+ v = f(123)
+ match v
+ | `std.Some x: std.exit(x)
+ | `std.None: std.exit(123)
+ ;;
+}
+
--- /dev/null
+++ b/test/stdopt-none.myr
@@ -1,0 +1,13 @@
+use std
+
+const f = {
+ -> `std.None
+}
+
+const main = {
+ match f()
+ | `std.Some x: std.exit(x)
+ | `std.None: std.exit(42)
+ ;;
+}
+
--- /dev/null
+++ b/test/stdopt-some.myr
@@ -1,0 +1,9 @@
+use std
+
+const main = {
+ match `std.Some 42
+ | `std.Some x: std.exit(x)
+ | `std.None: std.exit(1)
+ ;;
+}
+
--- /dev/null
+++ b/test/stdopt-struct.myr
@@ -1,0 +1,9 @@
+use std
+
+type t = struct
+ next : std.option(int)
+;;
+
+const main = {
+ std.exit(42)
+}
--- /dev/null
+++ b/test/stdsearch.myr
@@ -1,0 +1,33 @@
+use std
+
+const sl = [1, 3, 5, 8, 9, 33]
+
+const main = {
+
+ expect(std.lsearch(sl[:], 1, std.numcmp), `std.Some 0)
+ expect(std.lsearch(sl[:], 33, std.numcmp), `std.Some sl.len - 1)
+ expect(std.lsearch(sl[:], 5, std.numcmp), `std.Some 2)
+ expect(std.lsearch(sl[:], 6, std.numcmp), `std.None)
+
+ expect(std.bsearch(sl[:], 1, std.numcmp), `std.Some 0)
+ expect(std.bsearch(sl[:], 33, std.numcmp), `std.Some sl.len - 1)
+ expect(std.bsearch(sl[:], 5, std.numcmp), `std.Some 2)
+ expect(std.bsearch(sl[:], 6, std.numcmp), `std.None)
+}
+
+const expect = {a, b
+ match a
+ | `std.None:
+ match b
+ | `std.Some x: std.fatal(1, "Expected `std.None, `std.None, got `std.None, `std.Some %i\n", x)
+ ;;
+ | `std.Some x:
+ match b
+ | `std.None: std.fatal(1, "Expected `std.Some %i, `std.Some %i, got `std.Some %i, `std.None\n", x, x, x)
+ | `std.Some y:
+ if x != y
+ std.fatal(1, "Expected `std.Some %i, `std.Some %i, got `std.Some %i, `std.Some %i\n", x, x, x, y)
+ ;;
+ ;;
+ ;;
+}
--- /dev/null
+++ b/test/stdslcp.myr
@@ -1,0 +1,19 @@
+use std
+
+const main = {
+ var a = [1,2,3,4,5]
+ var b = [6,7,8,9,10]
+
+
+ std.slcp(a[:a.len-2], a[2:])
+ std.slcp(b[2:], b[:b.len-2])
+
+ for x in a
+ std.put("%i ", x)
+ ;;
+ std.put("\n")
+ for x in b
+ std.put("%i ", x)
+ ;;
+ std.put("\n")
+}
--- /dev/null
+++ b/test/stdsort.myr
@@ -1,0 +1,38 @@
+use std
+
+const main = {
+ var a = [
+ 3, 5, 4, 9, 7, 2, 6, 0, 1, 8,
+ ]
+ var b = [
+ 3, 4, 5, 1, 2, 6, 7, 8, 9, 10
+ ]
+ var c = [
+ "a", "aa", "b", "C", "Cc", "cC", "d", "f", "fuckit", "go",
+ ]
+
+ std.sort(a[:], intcmp)
+ for v in a
+ std.put("%i\n", v)
+ ;;
+ std.sort(b[:], std.numcmp)
+ for v in b
+ std.put("%i\n", v)
+ ;;
+ std.sort(c[:], std.strcmp)
+ for v in c
+ std.put("%s\n", v)
+ ;;
+}
+
+const intcmp = {a, b
+ if a < b
+ -> `std.Before
+ elif a == b
+ -> `std.Equal
+ else
+ -> `std.After
+ ;;
+}
+
+
--- /dev/null
+++ b/test/stdtry.myr
@@ -1,0 +1,8 @@
+use std
+
+const main = {
+ var x = `std.Some 123
+ std.try(x)
+ x = `std.None
+ std.try(x)
+}
--- /dev/null
+++ b/test/str.myr
@@ -1,0 +1,9 @@
+use std
+/* checks that string literals are compiled correctly.
+exits with ascii 'f', ie, 102. */
+const main = {
+ var str
+
+ str = "asdf"
+ std.exit(str[3] castto(int))
+}
--- /dev/null
+++ b/test/strfind.myr
@@ -1,0 +1,24 @@
+use std
+
+const main = {
+ printloc(std.strfind("", ""))
+ printloc(std.strfind("", "a"))
+ printloc(std.strfind("ab", "abc"))
+ printloc(std.strfind("abc", "abc"))
+ printloc(std.strfind("abcde", "abc"))
+ printloc(std.strfind("abcde", "xyz"))
+ printloc(std.strfind("abcde", "bcd"))
+ printloc(std.strfind("abcde", "cde"))
+ printloc(std.strfind("abcde", "def"))
+ printloc(std.strfind("abcde", "abx"))
+}
+
+const printloc = {l
+ match l
+ | `std.Some loc: std.put("Found %z\n", loc)
+ | `std.None: std.put("No match\n")
+ ;;
+}
+
+
+
--- /dev/null
+++ b/test/strjoin.myr
@@ -1,0 +1,13 @@
+use std
+
+const main = {
+ const strings = [
+ "x",
+ "y",
+ "z",
+ "w"
+ ]
+ std.put("%s\n", std.strcat("foo;", "bar"))
+ std.put("%s\n", std.strjoin(strings[:], ""))
+ std.put("%s\n", std.strjoin(strings[:], ":"))
+}
--- /dev/null
+++ b/test/strsplit.myr
@@ -1,0 +1,24 @@
+use std
+
+const main = {
+ var i
+ var sp
+
+ sp = std.strsplit("a,b,c,d", ",")
+ for i = 0; i < sp.len; i++
+ std.put("\"%s\"\n", sp[i])
+ ;;
+ std.slfree(sp)
+
+ sp = std.strsplit("a,,b,c,,d", ",")
+ for i = 0; i < sp.len; i++
+ std.put("\"%s\"\n", sp[i])
+ ;;
+ std.slfree(sp)
+
+ sp = std.strsplit("a--b---b--c---c-d--d", "---")
+ for i = 0; i < sp.len; i++
+ std.put("\"%s\"\n", sp[i])
+ ;;
+ std.slfree(sp)
+}
--- /dev/null
+++ b/test/strstrip.myr
@@ -1,0 +1,22 @@
+use std
+
+const main = {
+ std.put("\"%s\"\n", std.strstrip(" abc "))
+ std.put("\"%s\"\n", std.strfstrip(" abc "))
+ std.put("\"%s\"\n", std.strrstrip(" abc "))
+
+ std.put("--\n")
+ std.put("\"%s\"\n", std.strstrip(" 世界 "))
+ std.put("\"%s\"\n", std.strfstrip(" 世界 "))
+ std.put("\"%s\"\n", std.strrstrip(" 世界 "))
+
+ std.put("--\n")
+ std.put("\"%s\"\n", std.strstrip(" "))
+ std.put("\"%s\"\n", std.strfstrip(" "))
+ std.put("\"%s\"\n", std.strrstrip(" "))
+
+ std.put("--\n")
+ std.put("\"%s\"\n", std.strstrip(""))
+ std.put("\"%s\"\n", std.strfstrip(""))
+ std.put("\"%s\"\n", std.strrstrip(""))
+}
--- /dev/null
+++ b/test/strtab.myr
@@ -1,0 +1,16 @@
+use std
+
+const strtab = [
+ "foo",
+ "bar",
+ "baz",
+ "quux"
+]
+
+const main = {
+ var i
+ for i = 0; i < strtab.len; i++
+ std.put("%i: %s\n", i, strtab[i])
+ ;;
+ std.exit(0)
+}
--- /dev/null
+++ b/test/struct.myr
@@ -1,0 +1,13 @@
+use std
+/* test reading and writing to struct members. exits with 42. */
+type pair = struct
+ a : int
+ b : int
+;;
+
+const main = {
+ var s : pair
+ s.a = 12
+ s.b = 30
+ std.exit(s.a + s.b)
+}
--- /dev/null
+++ b/test/struct1.myr
@@ -1,0 +1,13 @@
+use std
+/*
+make sure assigning to a 1-element struct works; exit status should be 12
+*/
+type val = struct
+ a : int
+;;
+
+const main = {
+ var s : val
+ s.a = 12
+ std.exit(s.a)
+}
--- /dev/null
+++ b/test/structarray.myr
@@ -1,0 +1,14 @@
+use std
+/* tests a struct containing an array. exit status should be 42. */
+type t = struct
+ a : int[42]
+;;
+
+const main = {
+ var v : t
+
+ v.a[0] = 11
+ v.a[1] = 20
+
+ std.exit(2*v.a[0] + v.a[1])
+}
--- /dev/null
+++ b/test/structasn.myr
@@ -1,0 +1,15 @@
+use std
+/* tests block assignment of structs. exits with 42.*/
+type pair = struct
+ a : int
+ b : int
+;;
+
+const main = {
+ var x : pair
+ var y : pair
+ x.a = 12
+ x.b = 30
+ y = x
+ std.exit(y.a + y.b)
+}
--- /dev/null
+++ b/test/structlit.myr
@@ -1,0 +1,17 @@
+use std
+
+/* checks that we can create struct literals with named initializers.
+ exits with 42. */
+type t = struct
+ a : int
+ b : char
+ c : byte[:]
+;;
+
+const main = {
+ var v : t
+
+ v = [.a=42, .b='x', .c="foo"]
+ std.exit(v.a)
+}
+
--- /dev/null
+++ b/test/structptr.myr
@@ -1,0 +1,17 @@
+use std
+/* tests reading and writing through a struct pointer. exits with 42. */
+type pair = struct
+ a : int
+ b : int
+;;
+
+const frob = {s
+ s.a = 12
+ s.b = 30
+}
+
+const main = {
+ var s : pair
+ frob(&s)
+ std.exit(s.a + s.b)
+}
--- /dev/null
+++ b/test/structret.myr
@@ -1,0 +1,21 @@
+use std
+/* tests returning large structs. should exit with 42. */
+type pair = struct
+ a : int
+ b : int
+;;
+
+const f = {
+ var s
+
+ s.a = 12
+ s.b = 30
+ -> s
+}
+
+const main = {
+ var s : pair
+
+ s = f()
+ std.exit(s.a + s.b)
+}
--- /dev/null
+++ b/test/subrangefor.myr
@@ -1,0 +1,8 @@
+use std
+
+const main = {
+ for i in [1,2,3,4][:2]
+ std.put("%i", i)
+ ;;
+ std.put("\n")
+}
--- /dev/null
+++ b/test/swidencast.myr
@@ -1,0 +1,10 @@
+use std
+/* sign extending cast. should exit with status 99 */
+const main = {
+ var u : int8
+ var v : int32
+
+ u = 99
+ v = u castto(int32)
+ std.exit(v castto(int))
+}
--- /dev/null
+++ b/test/tests
@@ -1,0 +1,142 @@
+# Format:
+# [B|F] testname [E|P] result
+# [B|F]: Compiler outcome.
+# B: Expect that this test will build.
+# F: Expect that this test will not build.
+# testname: Test case
+# The test that will run. We will try to
+# compile 'testname.myr' to 'testname',
+# and then execute it, verifying the result
+# [E|P|C]: Result type
+# E tells us that the result is an exit status
+# P tells us that the result is on stdout,
+# and should be compared to the value on the
+# line
+# C tells us that the result is on stdout,
+# and should be compared to the contents of
+# the file passed on the line.
+# result: Result value
+# What we compare with. This should be self-
+# evident.
+B main E 0
+B splitline E 3
+B add E 53
+B mul E 42
+B mul8 E 18
+B div E 42
+B mod E 6
+B bsr E 5
+B chartest E 0
+B trunccast E 15
+B zwidencast E 99
+B swidencast E 99
+B derefassign E 123
+B ptrpreinc E 9
+B incret E 1
+B outparam E 42
+B outparam-sl E 2
+B struct1 E 12
+B struct E 42
+B align C
+B structptr E 42
+B structasn E 42
+B structarray E 42
+B structret E 42
+B constslice P 2312345678
+B exportmain E 42
+B slalloc E 123
+B neststruct E 3
+B array E 7
+B arrayaddr E 42
+B global-arrayvar E 7
+B arraylen E 12
+B slice E 10
+B slicelen E 5
+B call E 42
+B doublecall P 42,33
+B voidcall E 12
+B callbig E 42
+B nestfn E 42
+# B closure E 55 ## BUGGERED
+B loop P 0123401236789
+B subrangefor P 12
+B patiter P 23512
+B condiftrue E 7
+B condiffalse E 9
+B condifrel E 7
+B overlappingif E 2
+B fib E 21
+B basicfloat E 84
+B sqrt E 4
+B log-and E 0
+B log-or E 1
+B str E 102
+B generic E 42
+B genericval E 42
+B trait-builtin E 42
+B emptytrait E 123
+B traitimpl P 246,44
+# B compoundimpl P intptr,charptr BUGGERED
+B nestucon P asdf
+B mkunion E 0
+B genericcall E 42
+B generictype E 0
+B genericret E 42
+B genericmatch E 15
+B genericrec E 0
+B stdopt-some E 42
+B stdopt-none E 42
+B stdopt-mk E 42
+B stdopt-struct E 42
+B sizeof E 4
+B gsizeof E 5
+B matchint E 84
+B matchconst E 88
+B matchunion E 84
+B matchtup E 42
+B matchstruct E 42
+B matcharray E 42
+B matchargunion E 69
+B matchargstr C
+B matchunion_sl P foo
+B matchbind E 8
+F matchmixed
+B bigliteral P 34359738368
+B arraylit-ni E 2
+B livearraylit E 21
+# B arraylit E 3 ## BUGGERED
+B structlit E 42
+B livestructlit E 21
+B tuple E 42
+B slgrow E 42
+B tyrec E 42
+B infer-named E 99
+B exportcycle E 0
+B import-type E 0
+B helloworld P Hello-世界
+B encodechar P 1世界äa
+B stdsearch E 0
+B stdtry C
+B strtab C
+B catfile C
+B stdsort C
+B strstrip C
+B strsplit C
+B strfind C
+B strjoin C
+B stdslcp C
+B bigint C
+B exporttrait
+# B local-labels E 10 ## BUGGERED
+F declmismatch
+F infermismatch
+# F usedef ## BUGGERED
+F occur
+F tyoccur
+F union-extraarg
+F union-missingarg
+F match-badtypes
+F generic-in-const
+F aritylow
+F arityhigh
+F badop
--- /dev/null
+++ b/test/trait-builtin.myr
@@ -1,0 +1,28 @@
+use std
+/* checks that generic types with traits are compiled correctly.
+without the 'numeric' trait on '@a', the '>' operator would not work
+within max. without the 'tctest' trait on '@a' in intlike_is42,
+comparing to 42 wouldn't work.
+
+exits with 42.
+*/
+
+generic max = {a:@a::numeric, b:@a::numeric
+ if a > b
+ -> a
+ else
+ -> b
+ ;;
+}
+
+generic intlike_is42 = {a : @a::(numeric,integral)
+ -> a == 42
+}
+
+const main = {
+ if intlike_is42(123)
+ std.exit(16)
+ else
+ std.exit(max(12, 42))
+ ;;
+}
--- /dev/null
+++ b/test/traitimpl.myr
@@ -1,0 +1,29 @@
+use std
+
+trait frobable @a =
+ frob : (val : @a -> @a)
+;;
+
+impl frobable int =
+ frob = {val
+ -> val * 2
+ }
+;;
+
+impl frobable int16 =
+ frob = {val
+ -> val * 4
+ }
+;;
+
+generic foo = {x : @a::frobable
+ -> frob(x)
+}
+
+const main = {
+ var a, b
+ a = foo(123)
+ b = foo(11 castto(int16))
+ std.put("%i,%w\n", a, b)
+}
+
--- /dev/null
+++ b/test/trunccast.myr
@@ -1,0 +1,10 @@
+use std
+/* should truncate y when casting to x, exiting with status 15 */
+const main = {
+ var x : int8
+ var y : int32
+
+ y = 9999
+ x = y castto(int8)
+ std.exit((x % 73) castto(int))
+}
--- /dev/null
+++ b/test/tuple.myr
@@ -1,0 +1,13 @@
+use std
+/* checks that we can create tuples and destructure them. exits with 42. */
+const main = {
+ var v
+ var x
+ var a
+ var b
+
+ x = 15
+ v = (x, x + 12)
+ (a, b) = v
+ std.exit(a + b)
+}
--- /dev/null
+++ b/test/tyoccur.myr
@@ -1,0 +1,11 @@
+use std
+/* checks that types do not contain themselves
+inline, because that would lead to an infinite
+sized type.
+*/
+
+type t = struct
+ memb : t
+;;
+
+var v : t
--- /dev/null
+++ b/test/tyrec.myr
@@ -1,0 +1,10 @@
+use std
+/* we just want to see if this file compiles */
+type foo = struct
+ v : foo#
+;;
+
+const main = {
+ var v : foo
+ std.exit(42)
+}
--- /dev/null
+++ b/test/union-extraarg.myr
@@ -1,0 +1,13 @@
+use std
+/*
+should fail to compile becuse
+we're constructing a union that
+with too many arguments.
+*/
+type u = union
+ `Foo
+;;
+
+const main = {
+ `Foo 123
+}
--- /dev/null
+++ b/test/union-missingarg.myr
@@ -1,0 +1,8 @@
+use std
+type u = union
+ `Foo int
+;;
+
+const main = {
+ `Foo
+}
--- /dev/null
+++ b/test/union.myr
@@ -1,0 +1,10 @@
+type u = union
+ `Some int
+ `None
+;;
+
+const main = {
+ var v : u
+
+ v = `None
+}
--- /dev/null
+++ b/test/usedef.myr
@@ -1,0 +1,8 @@
+/*
+should fail to compile because 'a' is used
+before it is defined.
+*/
+const main = {
+ var a : int
+ std.exit(a)
+}
--- /dev/null
+++ b/test/voidcall.myr
@@ -1,0 +1,13 @@
+use std
+/* checks that calling void functions works. should compile, and not die
+when running. the exit value is 12, but it's really a dummy. */
+const f = {
+ var a
+
+ a = a + 1
+}
+
+const main = {
+ f()
+ std.exit(12)
+}
--- /dev/null
+++ b/test/zwidencast.myr
@@ -1,0 +1,10 @@
+use std
+/* should zero-extend u when casting to v, returning 99 */
+const main = {
+ var u : uint8
+ var v : uint32
+
+ u = 99
+ v = u castto(uint32)
+ std.exit(v castto(int))
+}