#define Extern #include "limbo.h" #include "y.tab.h" enum { Leof = -1, Linestart = 0, Mlower = 1, Mupper = 2, Munder = 4, Malpha = Mupper|Mlower|Munder, Mdigit = 8, Msign = 16, Mexp = 32, Mhex = 64, Mradix = 128, HashSize = 1024, MaxPath = 4096, }; typedef struct Keywd Keywd; struct Keywd { char *name; int token; }; File **files; /* files making up the module, sorted by absolute line */ int nfiles; static int lenfiles; static int lastfile; /* index of last file looked up */ static char *incpath[MaxIncPath]; static Sym *symbols[HashSize]; static Sym *strings[HashSize]; static char map[256]; static Biobuf *bins[MaxInclude]; static Biobuf *bin; static Line linestack[MaxInclude]; static int lineno; static int linepos; static int bstack; static int dowarn; static int maxerr; static int ineof; static int dosym; static int toterrors; static char srcdir[MaxPath]; static char *outfile; static char *symfile; static Keywd keywords[] = { "adt", Ladt, "alt", Lalt, "array", Larray, "big", Ltid, "break", Lbreak, "byte", Ltid, "case", Lcase, "chan", Lchan, "con", Lcon, "continue", Lcont, "cyclic", Lcyclic, "do", Ldo, "else", Lelse, "exit", Lexit, "fn", Lfn, "for", Lfor, "hd", Lhd, "if", Lif, "implement", Limplement, "import", Limport, "include", Linclude, "int", Ltid, "len", Llen, "list", Llist, "load", Lload, "module", Lmodule, "nil", Lnil, "of", Lof, "or", Lor, "real", Ltid, "ref", Lref, "return", Lreturn, "self", Lself, "spawn", Lspawn, "string", Ltid, "tl", Ltl, "to", Lto, "type", Ltype, "while", Lwhile, 0, }; static char* mkfileext(char *file, char *oldext, char *ext) { char *ofile; int n, n2; n = strlen(file); n2 = strlen(oldext); if(n >= n2 && strcmp(&file[n-n2], oldext) == 0) n -= n2; ofile = malloc(n + strlen(ext) + 1); memmove(ofile, file, n); strcpy(ofile+n, ext); return ofile; } void trapFPE(unsigned exception[5], int value[2]) { /* can't happen; it's just here to keep FPinit happy. */ USED(exception); USED(value); } static void usage(void) { fprint(2, "usage: limbo [-CGSacgwe] [-I incdir] [-o outfile] [-{T|t} module] [-D debug] file ...\n"); exits("usage"); } static int isfatal; void main(int argc, char *argv[]) { char *s, *ofile, *ext; int i; FPinit(); FPcontrol(0, INVAL|ZDIV|OVFL|UNFL|INEX); fmtinstall('A', addrconv); fmtinstall('D', dotconv); fmtinstall('I', instconv); fmtinstall('K', declconv); fmtinstall('k', storeconv); fmtinstall('L', lineconv); fmtinstall('M', mapconv); fmtinstall('n', nodeconv); /* exp structure */ fmtinstall('N', nameconv); /* decl name */ fmtinstall('O', opconv); fmtinstall('g', gfltconv); fmtinstall('Q', ntconv); /* src expression with type */ fmtinstall('R', ctypeconv); /* c equivalent type */ fmtinstall('T', typeconv); /* source style types */ fmtinstall('t', stypeconv); /* structurally descriptive type */ fmtinstall('U', srcconv); fmtinstall('v', expconv); /* src expression */ fmtinstall('V', expconv); /* src expression in '' */ lexinit(); optabinit(); gendis = 1; asmsym = 0; maxerr = 20; ofile = nil; ext = nil; ARGBEGIN{ case 'D': s = ARGF(); while(s && *s) debug[*s++] = 1; break; case 'I': s = ARGF(); if(s == nil) usage(); addinclude(s); break; case 'G': asmsym = 1; break; case 'S': gendis = 0; break; case 'a': emitstub = 1; break; case 'c': mustcompile = 1; break; case 'C': dontcompile = 1; break; case 'e': maxerr = 1000; break; case 'f': isfatal = 1; break; case 'g': dosym = 1; break; case 'o': ofile = ARGF(); break; case 's': s = ARGF(); if(s != nil) fixss = atoi(s); break; case 't': emittab = ARGF(); if(emittab == nil) usage(); break; case 'T': emitcode = ARGF(); if(emitcode == nil) usage(); break; case 'w': superwarn = dowarn; dowarn = 1; break; case 'x': ext = ARGF(); break; case 'X': signdump = ARGF(); break; default: usage(); break; }ARGEND addinclude(INCPATH); if(argc == 0){ usage(); }else if(ofile != nil){ if(argc != 1) usage(); translate(argv[0], ofile, mkfileext(ofile, ".dis", ".sbl")); }else{ if(ext == nil){ ext = ".s"; if(gendis) ext = ".dis"; } for(i = 0; i < argc; i++){ s = strrchr(argv[i], '/'); if(s == nil) s = argv[i]; else s++; if(argc > 1) print("%s:\n", argv[i]); ofile = mkfileext(s, ".b", ext); translate(argv[i], ofile, mkfileext(ofile, ".dis", ".sbl")); } } if(toterrors) exits("errors"); exits(0); } void translate(char *in, char *out, char *dbg) { outfile = out; symfile = dbg; errors = 0; bins[0] = Bopen(in, OREAD); if(bins[0] == nil){ fprint(2, "can't open %s: %r\n", in); toterrors++; return; } if(!emitstub && !emittab && !emitcode){ bout = Bopen(out, OWRITE); if(bout == nil){ fprint(2, "can't open %s: %r\n", out); toterrors++; Bterm(bins[0]); return; } if(dosym){ bsym = Bopen(dbg, OWRITE); if(bsym == nil) fprint(2, "can't open %s: %r\n", dbg); } } lexstart(in); popscopes(); typestart(); genstart(); declstart(); yyparse(); modcom(); if(bout != nil) Bterm(bout); toterrors += errors; if(errors && bout != nil) remove(out); if(errors && bsym != nil) remove(dbg); } static int Getc(void) { int c; if(ineof) return Beof; c = BGETC(bin); if(c == Beof) ineof = 1; linepos++; return c; } static void unGetc(void) { if(ineof) return; Bungetc(bin); linepos--; } static int getrune(void) { int c; if(ineof) return Beof; c = Bgetrune(bin); if(c == Beof) ineof = 1; linepos++; return c; } static void ungetrune(void) { if(ineof) return; Bungetrune(bin); linepos--; } void addinclude(char *s) { int i; for(i = 0; i < MaxIncPath; i++){ if(incpath[i] == 0){ incpath[i] = s; return; } } fatal("out of include path space"); } static File* mkfile(char *name, int abs, int off, int in, char *act, int actoff, int sbl) { File *f; f = allocmem(sizeof *f); f->name = name; f->abs = abs; f->off = off; f->in = in; f->act = act; f->actoff = actoff; f->sbl = sbl; return f; } static int addfile(File *f) { if(nfiles >= lenfiles){ lenfiles = nfiles+32; files = reallocmem(files, lenfiles*sizeof(File*)); } files[nfiles] = f; return nfiles++; } void includef(Sym *file) { Biobuf *b; char *p, buf[MaxPath]; int i; linestack[bstack].line = lineno; linestack[bstack].pos = linepos; bstack++; if(bstack >= MaxInclude) fatal("%L: include file depth too great", curline()); p = ""; if(file->name[0] != '/') p = srcdir; seprint(buf, buf+sizeof(buf), "%s%s", p, file->name); b = Bopen(buf, OREAD); for(i = 0; b == nil && incpath[i] != nil && file->name[0] != '/'; i++){ seprint(buf, buf+sizeof(buf), "%s/%s", incpath[i], file->name); b = Bopen(buf, OREAD); } bins[bstack] = b; if(bins[bstack] == nil){ yyerror("can't include %s: %r", file->name); bstack--; }else{ addfile(mkfile(strdup(buf), lineno+1, -lineno, lineno, nil, 0, -1)); lineno++; linepos = Linestart; } bin = bins[bstack]; } /* * we hit eof in the current file * revert to the file which included it. */ static void popinclude(void) { Fline fl; File *f; int oline, opos, ln; ineof = 0; bstack--; bin = bins[bstack]; oline = linestack[bstack].line; opos = linestack[bstack].pos; fl = fline(oline); f = fl.file; ln = fl.line; lineno++; linepos = opos; addfile(mkfile(f->name, lineno, ln-lineno, f->in, f->act, f->actoff, -1)); } /* * convert an absolute Line into a file and line within the file */ Fline fline(int absline) { Fline fl; int l, r, m, s; if(absline < files[lastfile]->abs || lastfile+1 < nfiles && absline >= files[lastfile+1]->abs){ lastfile = 0; l = 0; r = nfiles - 1; while(l <= r){ m = (r + l) / 2; s = files[m]->abs; if(s <= absline){ l = m + 1; lastfile = m; }else r = m - 1; } } fl.file = files[lastfile]; fl.line = absline + files[lastfile]->off; return fl; } /* * read a comment */ static int lexcom(void) { File *f; char buf[StrSize], *s, *t, *act; int i, n, c, actline; i = 0; while((c = Getc()) != '\n'){ if(c == Beof) return -1; if(i < sizeof(buf)-1); buf[i++] = c; } buf[i] = 0; lineno++; linepos = Linestart; if(strncmp(buf, "line ", 5) != 0 && strncmp(buf, "line\t", 5) != 0) return 0; for(s = buf+5; *s == ' ' || *s == '\t'; s++) ; if(!(map[*s] & Mdigit)) return 0; n = 0; for(; map[c = *s] & Mdigit; s++) n = n * 10 + c - '0'; for(; *s == ' ' || *s == '\t'; s++) ; if(*s != '"') return 0; s++; t = strchr(s, '"'); if(t == nil || t[1] != '\0') return 0; *t = '\0'; f = files[nfiles - 1]; if(n == f->off+lineno && strcmp(s, f->name) == 0) return 1; act = f->name; actline = lineno + f->off; if(f->act != nil){ actline += f->actoff; act = f->act; } addfile(mkfile(strdup(s), lineno, n-lineno, f->in, act, actline - n, -1)); return 1; } Line curline(void) { Line line; line.line = lineno; line.pos = linepos; return line; } int lineconv(va_list *arg, Fconv *f) { Fline fl; File *file; Line inl, line; char buf[StrSize], *s; line = va_arg(*arg, Line); if(line.line < 0){ strconv("", f); return 0; } fl = fline(line.line); file = fl.file; s = seprint(buf, buf+sizeof(buf), "%s:%d", file->name, fl.line); if(file->act != nil) s = seprint(s, buf+sizeof(buf), " [%s:%d]", file->act, file->actoff+fl.line); if(file->in >= 0){ inl.line = file->in; inl.pos = 0; seprint(s, buf+sizeof(buf), ": %L", inl); } strconv(buf, f); return 0; } static char* posconv(char *s, char *e, Line line) { Fline fl; if(line.line < 0) return secpy(s, e, "nopos"); fl = fline(line.line); return seprint(s, e, "%s:%d.%d", fl.file->name, fl.line, line.pos); } int srcconv(va_list *arg, Fconv *f) { Src src; char buf[StrSize], *s; src = va_arg(*arg, Src); s = posconv(buf, buf+sizeof(buf), src.start); s = secpy(s, buf+sizeof(buf), ","); posconv(s, buf+sizeof(buf), src.stop); strconv(buf, f); return 0; } void lexstart(char *in) { char *p; ineof = 0; bstack = 0; nfiles = 0; lastfile = 0; addfile(mkfile(strdup(in), 1, 0, -1, nil, 0, -1)); bin = bins[bstack]; lineno = 1; linepos = Linestart; secpy(srcdir, srcdir+MaxPath, in); p = strrchr(srcdir, '/'); if(p == nil) srcdir[0] = '\0'; else p[1] = '\0'; } void lexinit(void) { Keywd *k; int i; for(i = 0; i < 256; i++){ if(i == '_' || i > 0xa0) map[i] |= Munder; if(i >= 'A' && i <= 'Z') map[i] |= Mupper; if(i >= 'a' && i <= 'z') map[i] |= Mlower; if(i >= 'A' && i <= 'F' || i >= 'a' && i <= 'f') map[i] |= Mhex; if(i == 'e' || i == 'E') map[i] |= Mexp; if(i == 'r' || i == 'R') map[i] |= Mradix; if(i == '-' || i == '+') map[i] |= Msign; if(i >= '0' && i <= '9') map[i] |= Mdigit; } memset(escmap, -1, sizeof(escmap)); escmap['\''] = '\''; unescmap['\''] = '\''; escmap['"'] = '"'; unescmap['"'] = '"'; escmap['\\'] = '\\'; unescmap['\\'] = '\\'; escmap['a'] = '\a'; unescmap['\a'] = 'a'; escmap['b'] = '\b'; unescmap['\b'] = 'b'; escmap['n'] = '\n'; unescmap['\n'] = 'n'; escmap['r'] = '\r'; unescmap['\r'] = 'r'; escmap['t'] = '\t'; unescmap['\t'] = 't'; escmap['v'] = '\v'; unescmap['\v'] = 'v'; escmap['0'] = '\0'; unescmap['\0'] = '0'; for(k = keywords; k->name != nil; k++) enter(k->name, k->token); } int lexid(int c) { Sym *sym; char id[StrSize*UTFmax+1], *p; Rune r; int i, t; p = id; i = 0; for(;;){ if(i < StrSize){ if(c < Runeself) *p++ = c; else{ r = c; p += runetochar(p, &r); } i++; } c = getrune(); if(c == Beof || c < 256 && !(map[c] & (Malpha|Mdigit))){ ungetrune(); break; } } *p = '\0'; sym = enter(id, Lid); t = sym->token; if(t == Lid || t == Ltid) yylval.tok.v.idval = sym; return t; } Long strtoi(char *t, int base) { char *s; Long v; int c, neg; neg = 0; if(t[0] == '-'){ neg = 1; t++; }else if(t[0] == '+') t++; v = 0; for(s = t; c = *s; s++){ if(map[c] & Mdigit) c -= '0'; else if(map[c] & Mlower) c = c - 'a' + 10; else if(map[c] & Mupper) c = c - 'A' + 10; if(c >= base){ yyerror("digit '%c' not radix %d", *s, base); return -1; } v = v * base + c; } if(neg) return -v; return v; } /* * parse a numeric identifier * format [0-9]+(r[0-9A-Za-z]+)? * or ([0-9]+(\.[0-9]*)?|\.[0-9]+)([eE][+-]?[0-9]+)? */ int lexnum(int c) { char buf[StrSize], *base; enum { Int, Radix, RadixSeen, Frac, ExpSeen, ExpSignSeen, Exp } state; double d; Long v; int i; i = 0; buf[i++] = c; state = Int; if(c == '.') state = Frac; base = nil; for(;;){ c = Getc(); if(c == Beof){ yyerror("end of file in numeric constant"); return Leof; } switch(state){ case Int: if(map[c] & Mdigit) break; if(map[c] & Mexp){ state = ExpSeen; break; } if(map[c] & Mradix){ base = &buf[i]; state = RadixSeen; break; } if(c == '.'){ state = Frac; break; } goto done; case RadixSeen: case Radix: if(map[c] & (Mdigit|Malpha)){ state = Radix; break; } goto done; case Frac: if(map[c] & Mdigit) break; if(map[c] & Mexp) state = ExpSeen; else goto done; break; case ExpSeen: if(map[c] & Msign){ state = ExpSignSeen; break; } /* fall through */ case ExpSignSeen: case Exp: if(map[c] & Mdigit){ state = Exp; break; } goto done; } if(i < StrSize-1) buf[i++] = c; } done: buf[i] = 0; unGetc(); switch(state){ default: yyerror("malformed numerical constant '%s'", buf); yylval.tok.v.ival = 0; return Lconst; case Radix: *base++ = '\0'; v = strtoi(buf, 10); if(v < 0) break; if(v < 2 || v > 36){ yyerror("radix '%s' must be between 2 and 36", buf); break; } v = strtoi(base, v); break; case Int: v = strtoi(buf, 10); break; case Frac: case Exp: d = strtod(buf, nil); yylval.tok.v.rval = d; return Lrconst; } yylval.tok.v.ival = v; return Lconst; } int escchar(void) { char buf[4+1]; int c, i; c = getrune(); if(c == Beof) return Beof; if(c == 'u'){ for(i = 0; i < 4; i++){ c = getrune(); if(c == Beof || !(map[c] & (Mdigit|Mhex))){ yyerror("malformed \\u escape sequence"); ungetrune(); break; } buf[i] = c; } buf[i] = 0; return strtoul(buf, 0, 16); } if(c < 256 && (i = escmap[c]) >= 0) return i; yyerror("unrecognized escape \\%C", c); return c; } void lexstring(void) { char *str; int c; Rune r; int len, alloc; alloc = 32; len = 0; str = allocmem(alloc * sizeof(str)); for(;;){ switch(c = getrune()){ case '\\': c = escchar(); if(c != Beof) break; /* fall through */ case Beof: yyerror("end of file in string constant"); yylval.tok.v.idval = enterstring(str, len); return; case '\n': yyerror("newline in string constant"); lineno++; linepos = Linestart; yylval.tok.v.idval = enterstring(str, len); return; case '"': yylval.tok.v.idval = enterstring(str, len); return; } while(len+UTFmax+1 >= alloc){ alloc += 32; str = reallocmem(str, alloc * sizeof(str)); } r = c; len += runetochar(&str[len], &r); str[len] = '\0'; } } static int lex(void) { int c; loop: yylval.tok.src.start.line = lineno; yylval.tok.src.start.pos = linepos; switch(c = getrune()){ case Beof: Bterm(bin); if(bstack == 0) return Leof; popinclude(); break; case '#': if(lexcom() < 0) return Leof; break; case '\n': lineno++; linepos = Linestart; goto loop; case ' ': case '\t': case '\r': case '\v': case '\f': goto loop; case '"': lexstring(); return Lsconst; case '\'': c = getrune(); if(c == '\\') c = escchar(); if(c == Beof){ yyerror("end of file in character constant"); return Beof; }else yylval.tok.v.ival = c; c = Getc(); if(c != '\'') { yyerror("missing closing '"); unGetc(); } return Lconst; case '(': case ')': case '[': case ']': case '{': case '}': case ',': case ';': case '~': return c; case ':': c = Getc(); if(c == ':') return Lcons; if(c == '=') return Ldeclas; unGetc(); return ':'; case '.': c = Getc(); unGetc(); if(c != Beof && (map[c] & Mdigit)) return lexnum('.'); return '.'; case '|': c = Getc(); if(c == '=') return Loreq; if(c == '|') return Loror; unGetc(); return '|'; case '&': c = Getc(); if(c == '=') return Landeq; if(c == '&') return Landand; unGetc(); return '&'; case '^': c = Getc(); if(c == '=') return Lxoreq; unGetc(); return '^'; case '*': c = Getc(); if(c == '=') return Lmuleq; unGetc(); return '*'; case '/': c = Getc(); if(c == '=') return Ldiveq; unGetc(); return '/'; case '%': c = Getc(); if(c == '=') return Lmodeq; unGetc(); return '%'; case '=': c = Getc(); if(c == '=') return Leq; if(c == '>') return Llabs; unGetc(); return '='; case '!': c = Getc(); if(c == '=') return Lneq; unGetc(); return '!'; case '>': c = Getc(); if(c == '=') return Lgeq; if(c == '>'){ c = Getc(); if(c == '=') return Lrsheq; unGetc(); return Lrsh; } unGetc(); return '>'; case '<': c = Getc(); if(c == '=') return Lleq; if(c == '-') return Lcomm; if(c == '<'){ c = Getc(); if(c == '=') return Llsheq; unGetc(); return Llsh; } unGetc(); return '<'; case '+': c = Getc(); if(c == '=') return Laddeq; if(c == '+') return Linc; unGetc(); return '+'; case '-': c = Getc(); if(c == '=') return Lsubeq; if(c == '-') return Ldec; if(c == '>') return Lmdot; unGetc(); return '-'; case '1': case '2': case '3': case '4': case '5': case '0': case '6': case '7': case '8': case '9': return lexnum(c); default: if(c >= 256 || (map[c] & Malpha)) return lexid(c); yyerror("unknown character %c", c); break; } goto loop; } int yylex(void) { int t; t = lex(); yylval.tok.src.stop.line = lineno; yylval.tok.src.stop.pos = linepos; return t; } Sym* enterstring(char *str, int n) { Sym *s; char *p, *e; ulong h; int c, c0; e = str + n; h = 0; for(p = str; p < e; p++){ c = *p; c ^= c << 6; h += (c << 11) ^ (c >> 1); c = *p; h ^= (c << 14) + (c << 7) + (c << 4) + c; } c0 = str[0]; h %= HashSize; for(s = strings[h]; s != nil; s = s->next){ if(s->name[0] == c0 && s->len == n && memcmp(s->name, str, n) == 0){ free(str); return s; } } if(n == 0) return enter("", 0); s = allocmem(sizeof(Sym)); memset(s, 0, sizeof(Sym)); s->name = str; s->len = n; s->next = strings[h]; strings[h] = s; return s; } int symcmp(Sym *s, Sym *t) { int n, c; n = s->len; if(n > t->len) n = t->len; c = memcmp(s->name, t->name, n); if(c == 0) return s->len - t->len; return c; } Sym* stringcat(Sym *s, Sym *t) { char *str; int n; n = s->len + t->len; str = allocmem(n); memmove(str, s->name, s->len); memmove(str+s->len, t->name, t->len); return enterstring(str, n); } Sym* enter(char *name, int token) { Sym *s; char *p; ulong h; int c0, c, n; c0 = name[0]; h = 0; for(p = name; c = *p; p++){ c ^= c << 6; h += (c << 11) ^ (c >> 1); c = *p; h ^= (c << 14) + (c << 7) + (c << 4) + c; } n = p - name; h %= HashSize; for(s = symbols[h]; s != nil; s = s->next) if(s->name[0] == c0 && strcmp(s->name, name) == 0) return s; s = allocmem(sizeof(Sym)); memset(s, 0, sizeof(Sym)); s->hash = h; s->name = allocmem(n+1); memmove(s->name, name, n+1); if(token == 0) token = Lid; s->token = token; s->next = symbols[h]; s->len = n; symbols[h] = s; return s; } int gfltconv(va_list *arg, Fconv *f) { double d; char buf[32]; d = va_arg(*arg, double); g_fmt(buf, d, 'e'); strconv(buf, f); return 0; } char* secpy(char *p, char *e, char *s) { int c; if(p == e){ p[-1] = '\0'; return p; } for(; c = *s; s++){ *p++ = c; if(p == e){ p[-1] = '\0'; return p; } } *p = '\0'; return p; } char* seprint(char *buf, char *end, char *fmt, ...) { va_list arg; if(buf == end) return buf; va_start(arg, fmt); buf = doprint(buf, end, fmt, arg); va_end(arg); return buf; } void warn(Line line, char *fmt, ...) { char buf[4096]; va_list arg; if(errors || !dowarn) return; va_start(arg, fmt); doprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); fprint(2, "%L: warning: %s\n", line, buf); } void nwarn(Node *n, char *fmt, ...) { char buf[4096]; va_list arg; if(errors || !dowarn) return; va_start(arg, fmt); doprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); fprint(2, "%L: warning: %s\n", n->src.start, buf); } void error(Line line, char *fmt, ...) { char buf[4096]; va_list arg; errors++; if(errors >= maxerr){ if(errors == maxerr) fprint(2, "too many errors, stopping\n"); return; } va_start(arg, fmt); doprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); fprint(2, "%L: %s\n", line, buf); } void nerror(Node *n, char *fmt, ...) { char buf[4096]; va_list arg; errors++; if(errors >= maxerr){ if(errors == maxerr) fprint(2, "too many errors, stopping\n"); return; } va_start(arg, fmt); doprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); fprint(2, "%L: %s\n", n->src.start, buf); } void yyerror(char *fmt, ...) { char buf[4096]; va_list arg; errors++; if(errors >= maxerr){ if(errors == maxerr) fprint(2, "too many errors, stopping\n"); return; } va_start(arg, fmt); doprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); fprint(2, "%L: %s\n", curline(), buf); } void fatal(char *fmt, ...) { char buf[4096]; va_list arg; va_start(arg, fmt); doprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); fprint(2, "fatal limbo compiler error: %s\n", buf); if(bout != nil) remove(outfile); if(bsym != nil) remove(symfile); if(isfatal) abort(); exits(buf); } void* allocmem(ulong n) { void *p; p = malloc(n); if(p == nil) fatal("out of memory"); return p; } void* reallocmem(void *p, ulong n) { if(p == nil) p = malloc(n); else p = realloc(p, n); if(p == nil) fatal("out of memory"); return p; }