#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 *bin; static Line linestack[MaxInclude]; static int lineno; static int linepos; static int bstack; static int ineof; static int lasttok; static YYSTYPE lastyylval; static char srcdir[MaxPath]; 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, "dynamic", Ldynamic, "else", Lelse, "exception", Lexcept, "exit", Lexit, "fixed", Lfix, "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, "pick", Lpick, "raise", Lraise, "raises", Lraises, "real", Ltid, "ref", Lref, "return", Lreturn, "self", Lself, "spawn", Lspawn, "string", Ltid, "tagof", Ltagof, "tl", Ltl, "to", Lto, "type", Ltype, "while", Lwhile, 0, }; static Keywd tokwords[] = { "&=", Landeq, "|=", Loreq, "^=", Lxoreq, "<<=", Llsheq, ">>=", Lrsheq, "+=", Laddeq, "-=", Lsubeq, "*=", Lmuleq, "/=", Ldiveq, "%=", Lmodeq, "**=", Lexpeq, ":=", Ldeclas, "||", Loror, "&&", Landand, "::", Lcons, "==", Leq, "!=", Lneq, "<=", Lleq, ">=", Lgeq, "<<", Llsh, ">>", Lrsh, "<-", Lcomm, "++", Linc, "--", Ldec, "->", Lmdot, "=>", Llabs, "**", Lexp, "EOF", Leof, "eof", Beof, 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['f'] = '\f'; unescmap['\f'] = 'f'; 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 cmap(int c) { if(c<0) return 0; if(c<256) return map[c]; return Mlower; } 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'; } 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"); } 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; } 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 && i < MaxIncPath && 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(!(cmap(*s) & Mdigit)) return 0; n = 0; for(; cmap(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(Fmt *f) { Fline fl; File *file; Line inl, line; char buf[StrSize], *s; line = va_arg(f->args, Line); if(line.line < 0) return fmtstrcpy(f, ""); 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); } return fmtstrcpy(f, buf); } 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(Fmt *f) { Src src; char buf[StrSize], *s; src = va_arg(f->args, Src); s = posconv(buf, buf+sizeof(buf), src.start); s = secpy(s, buf+sizeof(buf), ","); posconv(s, buf+sizeof(buf), src.stop); return fmtstrcpy(f, buf); } 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 || !(cmap(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, ck; neg = 0; if(t[0] == '-'){ neg = 1; t++; }else if(t[0] == '+') t++; v = 0; for(s = t; c = *s; s++){ ck = cmap(c); if(ck & Mdigit) c -= '0'; else if(ck & Mlower) c = c - 'a' + 10; else if(ck & 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; } static int digit(int c, int base) { int cc, ck; cc = c; ck = cmap(c); if(ck & Mdigit) c -= '0'; else if(ck & Mlower) c = c - 'a' + 10; else if(ck & Mupper) c = c - 'A' + 10; else if(ck & Munder) {} else return -1; if(c >= base) yyerror("digit '%c' not radix %d", cc, base); return c; } double strtodb(char *t, int base) { double num, dem; int neg, eneg, dig, exp, c, d; num = 0; neg = 0; dig = 0; exp = 0; eneg = 0; c = *t++; if(c == '-' || c == '+'){ if(c == '-') neg = 1; c = *t++; } while((d = digit(c, base)) >= 0){ num = num*base + d; c = *t++; } if(c == '.') c = *t++; while((d = digit(c, base)) >= 0){ num = num*base + d; dig++; c = *t++; } if(c == 'e' || c == 'E'){ c = *t++; if(c == '-' || c == '+'){ if(c == '-'){ dig = -dig; eneg = 1; } c = *t++; } while((d = digit(c, base)) >= 0){ exp = exp*base + d; c = *t++; } } exp -= dig; if(exp < 0){ exp = -exp; eneg = !eneg; } dem = rpow(base, exp); if(eneg) num /= dem; else num *= dem; if(neg) return -num; return num; } /* * 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, FracB } state; double d; Long v; int i, ck; 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; } ck = cmap(c); switch(state){ case Int: if(ck & Mdigit) break; if(ck & Mexp){ state = ExpSeen; break; } if(ck & Mradix){ base = &buf[i]; state = RadixSeen; break; } if(c == '.'){ state = Frac; break; } goto done; case RadixSeen: case Radix: if(ck & (Mdigit|Malpha)){ state = Radix; break; } if(c == '.'){ state = FracB; break; } goto done; case Frac: if(ck & Mdigit) break; if(ck & Mexp) state = ExpSeen; else goto done; break; case FracB: if(ck & (Mdigit|Malpha)) break; goto done; case ExpSeen: if(ck & Msign){ state = ExpSignSeen; break; } /* fall through */ case ExpSignSeen: case Exp: if(ck & 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; case FracB: *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; } d = strtodb(base, v); 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 || !(cmap(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(int israw) { char *str; int c, t, startlno; Rune r; int len, alloc; alloc = 32; len = 0; str = allocmem(alloc * sizeof(str)); startlno = lineno; for(;;){ c = getrune(); if(israw){ switch(c){ case '`': yylval.tok.v.idval = enterstring(str, len); return; case '\n': lineno++; linepos = Linestart; break; case Beof: t = lineno; lineno = startlno; yyerror("end of file in raw string constant"); lineno = t; yylval.tok.v.idval = enterstring(str, len); return; } }else{ switch(c){ 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; c = getrune(); /* ehg: outside switch() to avoid bug in VisualC++5.0 */ switch(c){ case Beof: Bterm(bin); if(bstack == 0) return Leof; popinclude(); break; case '#': if(lexcom() < 0){ Bterm(bin); if(bstack == 0) return Leof; popinclude(); } break; case '\n': lineno++; linepos = Linestart; goto loop; case ' ': case '\t': case '\r': case '\v': case '\f': goto loop; case '"': lexstring(0); return Lsconst; case '`': lexstring(1); 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 && (cmap(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; if(c == '*'){ c = Getc(); if(c == '=') return Lexpeq; unGetc(); return Lexp; } 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(cmap(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; lasttok = t; lastyylval = yylval; return t; } static char* toksp(int t) { Keywd *k; static char buf[256]; switch(t){ case Lconst: snprint(buf, sizeof(buf), "%lld", lastyylval.tok.v.ival); return buf; case Lrconst: snprint(buf, sizeof(buf), "%f", lastyylval.tok.v.rval); return buf; case Lsconst: snprint(buf, sizeof(buf), "\"%s\"", lastyylval.tok.v.idval->name); return buf; case Ltid: case Lid: return lastyylval.tok.v.idval->name; } for(k = keywords; k->name != nil; k++) if(t == k->token) return k->name; for(k = tokwords; k->name != nil; k++) if(t == k->token) return k->name; if(t < 0 || t > 255) fatal("bad token %d in toksp()", t); buf[0] = t; buf[1] = '\0'; return buf; } 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+1); memmove(str, s->name, s->len); memmove(str+s->len, t->name, t->len); str[n] = '\0'; 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; } char* stringpr(char *buf, char *end, Sym *sym) { char sb[30], *s, *p; int i, c, n; s = sym->name; n = sym->len; if(n > 10) n = 10; p = sb; *p++ = '"'; for(i = 0; i < n; i++){ c = s[i]; switch(c){ case '\\': case '"': case '\n': case '\r': case '\t': case '\b': case '\a': case '\v': case '\0': *p++ = '\\'; *p++ = unescmap[c]; break; default: *p++ = c; break; } } if(n != sym->len){ *p++ = '.'; *p++ = '.'; *p++ = '.'; } *p++ = '"'; *p = 0; return secpy(buf, end, sb); } void warn(Line line, char *fmt, ...) { char buf[4096]; va_list arg; if(errors || !dowarn) return; va_start(arg, fmt); vseprint(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); vseprint(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); vseprint(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); vseprint(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); vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); if(lasttok != 0) fprint(2, "%L: near ` %s ` : %s\n", curline(), toksp(lasttok), buf); else fprint(2, "%L: %s\n", curline(), buf); } void fatal(char *fmt, ...) { char buf[4096]; va_list arg; if(errors == 0 || isfatal){ va_start(arg, fmt); vseprint(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); } int gfltconv(Fmt *f) { double d; char buf[32]; d = va_arg(f->args, double); g_fmt(buf, d, 'e'); return fmtstrcpy(f, buf); } 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 = vseprint(buf, end, fmt, arg); va_end(arg); return 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; }