aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/cc/cc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/cc/cc.c')
-rw-r--r--src/cmd/cc/cc.c409
1 files changed, 409 insertions, 0 deletions
diff --git a/src/cmd/cc/cc.c b/src/cmd/cc/cc.c
new file mode 100644
index 0000000..8ad0022
--- /dev/null
+++ b/src/cmd/cc/cc.c
@@ -0,0 +1,409 @@
+#include "cc.h"
+#include <libn/macro/map.h>
+
+// -----------------------------------------------------------------------
+// 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
+void
+·free(void* _, void* ptr) {
+ return free(ptr);
+}
+
+static
+void *
+·alloc(void* _, uint n, ulong size) {
+ return malloc(n*size);
+}
+
+static
+void *
+·calloc(void* _, uint n, ulong size) {
+ return calloc(n, size);
+}
+
+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
+
+/* TODO: intern types for memory savings */
+int
+type()
+{
+ 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.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));
+
+ /* builtins */
+ C.builtin.vargs = (Decl) {
+ .pos = (Range) {
+ .beg = {
+ .col = 0,
+ .line = 0,
+ .path = "<builtin>",
+ },
+ .end = {
+ .col = 0,
+ .line = 0,
+ .path = "<builtin>",
+ },
+ },
+ .kind = Dtype,
+ .spec = Mtype,
+ .type = 1,
+ .name = "__builtin_va_list",
+ };
+
+ intern(&C.builtin.vargs.name);
+}
+
+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));
+
+ lx->sym = (SymTab){ 0 };
+}
+
+void
+freelx(Lexer *lx)
+{
+ free(lx->omit.path);
+}
+
+void
+initp(Parser *p)
+{
+ /* initialize temporary buffers */
+ memset(p->spstk, 0, sizeof(p->spstk));
+ memset(p->nmstk, 0, sizeof(p->nmstk));
+ memset(p->dtstk, 0, sizeof(p->dtstk));
+ memset(p->ptstk, 0, sizeof(p->ptstk));
+
+ p->sp = p->spstk;
+ p->nm = p->nmstk;
+ p->dt = p->dtstk;
+ p->pt = p->ptstk;
+
+ /* initialize ast */
+ p->ast.cap = 0;
+ p->ast.len = 0;
+ p->ast.decls = nil;
+}
+
+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";
+ }
+ }
+
+ initlx(&lx);
+ initp(&p);
+
+ 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) ? "<stdin>" : argv[0];
+ intern(&src);
+
+ if ((err = compile(src)), err) {
+ exit(2);
+ }
+
+ exit(0);
+}