#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]; } // ----------------------------------------------------------------------- // io buffer management #define asrdr(x) (io·Reader){(int (*)(void *, int, int, void *))x} // path should be absolute Io* openio(byte *path) { string *it, *end; Stream *f; intern(&path); // See if we have already opened file; // If so, and it hasn't been flagged return it for (it = C.omit.path, end = it + C.omit.len; it < end; ++it) { if ((uintptr)(*it) == (uintptr)(path)) return nil; } printf("OPENING PATH %s\n", path); if ((C.io - C.iostk) >= arrlen(C.iostk)-1) panicf("out of I/O space!"); C.io->f = io·open(path, "r"); if (!C.io->f) panicf("file %s not found", path); C.io->kind = IOfile; C.io->path = path; bufio·initreader(&C.io->buf, asrdr(io·read), C.io->f); return C.io++; } Io* makeio() { if ((C.io - C.iostk) >= arrlen(C.iostk)-1) panicf("out of I/O space!"); C.io->path = ""; C.io->buf = (io·Buffer) { .state = bufio·rdr | bufio·end, .runesize = 0, .h = nil, .size = bufio·size, .beg = C.io->buf.buf + bufio·ungets, .pos = C.io->buf.buf + bufio·ungets, .end = C.io->buf.buf + bufio·ungets, }; C.io->b = C.io->buf.beg; return C.io++; } #undef asrdr void freeio(Io *io) { if (io->kind & IOfile) { io·close(io->f); } io->kind = 0; io->link = nil; io->path = nil; io->store = (Pos){ 0 }; } void pushomit(string omit) { if (C.omit.len == C.omit.cap) { C.omit.cap += 20; C.omit.path = realloc(C.omit.path, C.omit.cap*sizeof(*C.omit.path)); } C.omit.path[C.omit.len++] = omit; } // ----------------------------------------------------------------------- // 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; } } // ----------------------------------------------------------------------- // 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); 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.omit.len = 0; C.omit.cap = 100; C.omit.path = calloc(C.omit.cap, sizeof(*C.inc.dir)); C.outfile = nil; C.io = C.iostk; C.io->link = nil; memset(C.iostk, 0, sizeof(C.iostk)); C.lxr = (Lexer){ 0 }; } error compile(byte *path) { Io *io; Token tok; byte *p, out[400]; intern(&path); strcpy(out, path); p = utf8·findrrune(out, '/'); if (p) *p++ = '\0'; else p = out; if (!C.outfile) { C.outfile = p; if (C.outfile) { if ((p = utf8·findrrune(C.outfile, '.'))) { p[0] = '.'; p[1] = 'o'; p[2] = '\0'; } } else { C.outfile = "/dev/null"; } } C.lxr.io = openio(path); C.lxr.pos = (Pos){ .path = path, .line = 1, .col = 1, }; while (tok = lex(&C.lxr), tok.kind > Aeof) { // puttok(tok); } return tok.kind != Anil; } 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); dodefine(&C.lxr, 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); }