#include "cc.h" #include // ----------------------------------------------------------------------- // string interning /* jenkins' one at a time hash */ static int32 hash_string(byte* s) { int32 h; h = 0; if (s != nil) { for (; *s; ++s) { h += *s; h = (h << 10); h = (h >> 6); } } h += (h << 3); h ^= (h >> 11); h += (h >> 11); return h; } static int streq(byte *s, byte *t) { if (s == nil) { if (t == nil) return 1; else return 0; } return (t == nil) ? 0 : strcmp(s, t) == 0; } #define HASH(s) hash_string(s) #define EQUAL(s, t) (streq(s, t)) static int getstr(string key, int *ok) { int idx; MAP_GET(idx, (&C.strs), key, HASH, EQUAL); *ok = idx < C.strs.n_buckets; return idx; } static int morestrtab(StrTab *tab, int n) { MAP_GROW(tab, string, int32, n, HASH, ·calloc, ·free, nil); } static int putstr(byte *s, error *err) { int sz; sz = C.strs.size; MAP_PUT((&C.strs), s, sz, HASH, EQUAL, morestrtab, err); } #undef HASH #undef EQUAL int32 intern(byte **s) { int i, ok; i = getstr(*s, &ok); if (ok) { *s = C.strs.keys[i]; goto END; } *s = str·make(*s); i = putstr(*s, &ok); C.strs.vals[i] = C.strs.size - 1; END: return C.strs.vals[i]; } // ----------------------------------------------------------------------- // type interning Type * type() { Type *t; if (C.type.len >= C.type.cap) { C.type.cap += 100; C.type.info = realloc(C.type.info, C.type.cap * sizeof(*C.type.info)); } return C.type.info + C.type.len++; } // ----------------------------------------------------------------------- // universal compiler builtins #define KEYWORD(a, b) b, byte *keywords[NUM_KEYWORDS] = { KEYWORDS }; #undef KEYWORD #define DIRECTIVE(a, b, c) b, byte *directives[NUM_DIRECTIVES] = { DIRECTIVES }; #undef DIRECTIVE struct Compiler C = { 0 }; // ----------------------------------------------------------------------- // cli flag handlers void pushinclude(byte *dirs) { string d, s, *it, *end; while (*dirs != '\0') { d = strchr(dirs, ' '); if (d != nil) *d = '\0'; s = dirs; intern(&s); for (it = C.inc.dir, end = it + C.inc.len; it != end; ++it) { if ((uintptr)s == (uintptr)(*it)) goto Nextdir; } if (C.inc.len == C.inc.cap) { C.inc.cap += 20; C.inc.dir = realloc(C.inc.dir, C.inc.cap*sizeof(*C.inc.dir)); } C.inc.dir[C.inc.len++] = s; Nextdir: if (d == nil) break; dirs = d + 1; } } // ----------------------------------------------------------------------- // error reporting void errorat(Pos x, byte *fmt, ...) { va_list args; va_start(args, fmt); printf("error:%s:%d:%d: ", os·basename(x.path), x.line, x.col); vprintf(fmt, args); printf("\n"); va_end(args); assert(0); } void warnat(Pos x, byte *fmt, ...) { va_list args; va_start(args, fmt); printf("warning:%s:%d:%d: ", os·basename(x.path), x.line, x.col); vprintf(fmt, args); printf("\n"); va_end(args); } // ----------------------------------------------------------------------- // main point of entry void init(void) { int i; for (i = 0; i < arrlen(keywords); i++) intern(&keywords[i]); for (i = 0; i < arrlen(directives); i++) intern(&directives[i]); C.heap = mem·makearena(mem·sys, nil); /* compiler definitions */ C.def.len = 0; C.def.cap = 100; C.def.val = calloc(C.def.cap, sizeof(*C.def.val)); /* compiler include paths */ C.inc.len = 0; C.inc.cap = 100; C.inc.dir = calloc(C.inc.cap, sizeof(*C.inc.dir)); C.inc.dir[C.inc.len++] = "."; C.outfile = nil; /* type info */ C.type.len = arrlen(basetypes); C.type.cap = 100 + arrlen(basetypes); C.type.info = calloc(C.type.cap, sizeof(*C.type.info)); memcpy(C.type.info, basetypes, C.type.len * sizeof(*C.type.info)); for (i = 0; i < arrlen(basetypes); i++) intern(&C.type.info[i].ident); } void initlx(Lexer *lx) { int i; memset(lx, 0, sizeof(*lx)); lx->b = lx->buf; /* predefine macros */ dodefine(lx, "__LINE__"); dodefine(lx, "__FILE__"); lx->macline = (uintptr)lookup(&lx->sym, "__LINE__"); lx->macfile = (uintptr)lookup(&lx->sym, "__FILE__"); for (i = 0; i < C.def.len; i++) dodefine(lx, C.def.val[i]); lx->omit.len = 0; lx->omit.cap = 100; lx->omit.path = calloc(lx->omit.cap, sizeof(*C.inc.dir)); lx->new = lx->iostk; lx->new->link = nil; memset(lx->iostk, 0, sizeof(lx->iostk)); } void freelx(Lexer *lx) { free(lx->omit.path); } void initp(Parser *p) { p->sp = p->spstk; p->nm = p->nmstk; p->dt = p->dtstk; } error compile(byte *path) { Lexer lx; Parser p; error err; byte *sep, out[400]; intern(&path); strcpy(out, path); sep = utf8·findrrune(out, '/'); if (sep) *sep++ = '\0'; else sep = out; if (!C.outfile) { C.outfile = sep; if (C.outfile) { if ((sep = utf8·findrrune(C.outfile, '.'))) { sep[0] = '.'; sep[1] = 'o'; sep[2] = '\0'; } } else { C.outfile = "/dev/null"; } } initp(&p); initlx(&lx); lx.io = openio(&lx, path); lx.pos = (Pos){ .path = path, .line = 1, .col = 1, }; err = parse(&p, &lx); freelx(&lx); return err; } error main(int argc, byte *argv[]) { byte *a, *src; int err; init(); ARGBEGIN { case 'o': C.outfile = ARGF(); break; case 'D': a = ARGF(); if (a) { intern(&a); if (C.def.len >= C.def.cap) { C.def.cap += 20; C.def.val = realloc(C.def.val, C.def.cap * sizeof(*C.def.val)); } C.def.val[C.def.len++] = a; } break; case 'I': a = ARGF(); if (a) pushinclude(a); break; } ARGEND if (argc < 1 && C.outfile == nil) { printf("usage: cc [-options] files\n"); exit(1); } // NOTE: This is just for my comfort during debugging. pushinclude("/home/nolln/root/include"); pushinclude("/home/nolln/root/include/vendor/libc"); src = (argc == 0) ? "" : argv[0]; intern(&src); if ((err = compile(src)), err) { exit(2); } exit(0); }