229 lines
5.8 KiB
C
229 lines
5.8 KiB
C
#include "gc.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static int
|
|
slurp(const char *path, char **outbuf, u64 *outlen)
|
|
{
|
|
FILE *f = fopen(path, "rb");
|
|
if (f == NULL) return -1;
|
|
fseek(f, 0, SEEK_END);
|
|
long n = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
if (n < 0) { fclose(f); return -1; }
|
|
char *b = malloc((size_t)n + 1);
|
|
if (b == NULL) { fclose(f); return -1; }
|
|
if (fread(b, 1, (size_t)n, f) != (size_t)n) { free(b); fclose(f); return -1; }
|
|
b[n] = '\0';
|
|
fclose(f);
|
|
*outbuf = b;
|
|
*outlen = (u64)n;
|
|
return 0;
|
|
}
|
|
|
|
struct importin {
|
|
const char *path;
|
|
const char *file;
|
|
char *buf;
|
|
u64 len;
|
|
};
|
|
|
|
static Node *
|
|
parseinput(Arena *a, const char *file, char *buf, u64 len,
|
|
const char *mod, const char *testsupport, int *bad)
|
|
{
|
|
Lex l;
|
|
Parser p;
|
|
lexinit(&l, a, file, buf, len);
|
|
parserinit(&p, a, &l);
|
|
p.testmodule = testsupport;
|
|
if (mod != NULL) {
|
|
p.pathmod = mod;
|
|
p.curmod = mod;
|
|
}
|
|
Node *f = parsefile(&p);
|
|
*bad = l.errs || p.errs;
|
|
return f;
|
|
}
|
|
|
|
static void
|
|
appendnodes(Node **head, Node **tail, Node *list)
|
|
{
|
|
if (list == NULL) return;
|
|
if (*head == NULL) *head = list;
|
|
else (*tail)->next = list;
|
|
while (list->next != NULL) list = list->next;
|
|
*tail = list;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
const char *src = NULL;
|
|
const char *out = NULL;
|
|
const char *wwiout = NULL; /* -I <out.wwi>: M2 export-data producer */
|
|
const char *testsupport = NULL;
|
|
int testmode = 0;
|
|
int testpackage = 0;
|
|
int entrymode = 0;
|
|
int sepmode = 0; /* -c: #22 M3 separate-compile / primary-
|
|
* only codegen (emit imported==0 decls
|
|
* only; treat `.wwi` deps as external) */
|
|
struct importin *imports = calloc((size_t)argc, sizeof *imports);
|
|
if (imports == NULL) {
|
|
fputs("w6c: out of memory\n", stderr);
|
|
return 1;
|
|
}
|
|
int nimports = 0;
|
|
for (int i = 1; i < argc; i++) {
|
|
const char *a = argv[i];
|
|
if (strcmp(a, "-o") == 0 && i + 1 < argc) {
|
|
out = argv[++i];
|
|
} else if (strcmp(a, "-I") == 0 && i + 1 < argc) {
|
|
wwiout = argv[++i];
|
|
} else if (strcmp(a, "-T") == 0) {
|
|
testmode = 1;
|
|
} else if (strcmp(a, "--test-package") == 0) {
|
|
testpackage = 1;
|
|
} else if (strcmp(a, "--entry") == 0) {
|
|
entrymode = 1;
|
|
} else if (strcmp(a, "--test-support-module") == 0) {
|
|
if (i + 1 >= argc) {
|
|
fputs("w6c: --test-support-module requires arg\n", stderr);
|
|
return 2;
|
|
}
|
|
testsupport = argv[++i];
|
|
} else if (strcmp(a, "-c") == 0) {
|
|
sepmode = 1;
|
|
} else if (strcmp(a, "--import") == 0) {
|
|
if (i + 2 >= argc) {
|
|
fputs("w6c: --import requires path and file\n", stderr);
|
|
return 2;
|
|
}
|
|
imports[nimports].path = argv[++i];
|
|
imports[nimports].file = argv[++i];
|
|
nimports++;
|
|
} else if (a[0] == '-') {
|
|
fprintf(stderr, "w6c: unknown flag %s\n", a);
|
|
return 2;
|
|
} else if (src == NULL) {
|
|
src = a;
|
|
} else {
|
|
fprintf(stderr, "w6c: only one input supported\n");
|
|
return 2;
|
|
}
|
|
}
|
|
if (src == NULL) {
|
|
fputs("usage: w6c [-T|--test-package] [--entry] [-c] [-I out.wwi] "
|
|
"[--import path dep.wwi]... [-o out.s] file.ww\n", stderr);
|
|
return 2;
|
|
}
|
|
if (nimports > 0 && !sepmode) {
|
|
fputs("w6c: --import requires -c\n", stderr);
|
|
return 2;
|
|
}
|
|
if ((entrymode || testpackage) && !sepmode) {
|
|
fputs("w6c: --entry and --test-package require -c\n", stderr);
|
|
return 2;
|
|
}
|
|
if (testmode && testpackage) {
|
|
fputs("w6c: -T and --test-package are mutually exclusive\n", stderr);
|
|
return 2;
|
|
}
|
|
for (int i = 0; i < nimports; i++) {
|
|
if (imports[i].path[0] == '\0') {
|
|
fputs("w6c: --import path is empty\n", stderr);
|
|
return 2;
|
|
}
|
|
if (i > 0 && strcmp(imports[i-1].path, imports[i].path) >= 0) {
|
|
fputs("w6c: --import paths must be sorted and unique\n", stderr);
|
|
return 2;
|
|
}
|
|
}
|
|
if (testsupport != NULL && (!sepmode
|
|
|| (strcmp(testsupport, "test") != 0
|
|
&& strcmp(testsupport, "__wwtest") != 0))) {
|
|
fputs("w6c: invalid --test-support-module\n", stderr);
|
|
return 2;
|
|
}
|
|
|
|
Arena *a = newarena();
|
|
Checker c;
|
|
Cg cg;
|
|
|
|
Node *head = NULL, *tail = NULL;
|
|
for (int i = 0; i < nimports; i++) {
|
|
if (slurp(imports[i].file, &imports[i].buf,
|
|
&imports[i].len) < 0) {
|
|
fprintf(stderr, "w6c: import %s: cannot read %s\n",
|
|
imports[i].path, imports[i].file);
|
|
return 1;
|
|
}
|
|
int bad = 0;
|
|
Node *f = parseinput(a, imports[i].file, imports[i].buf,
|
|
imports[i].len, imports[i].path, testsupport, &bad);
|
|
if (bad) return 1;
|
|
appendnodes(&head, &tail, f->list);
|
|
}
|
|
|
|
char *buf;
|
|
u64 len;
|
|
if (slurp(src, &buf, &len) < 0) {
|
|
fprintf(stderr, "w6c: %s: cannot read\n", src);
|
|
return 1;
|
|
}
|
|
int bad = 0;
|
|
Node *file = parseinput(a, src, buf, len, NULL, testsupport, &bad);
|
|
if (bad) return 1;
|
|
if (head != NULL) {
|
|
tail->next = file->list;
|
|
file->list = head;
|
|
}
|
|
|
|
check_init(&c, a);
|
|
c.is_test = testmode;
|
|
c.is_test_package = testpackage;
|
|
if (testsupport != NULL) c.test_module = testsupport;
|
|
c.sep_mode = sepmode;
|
|
check_file(&c, file);
|
|
if (c.errs) return 1;
|
|
|
|
/* M2 export-data: write the `.wwi` after a clean check, before cgen.
|
|
* Dead on the live path (no existing invocation passes -I); the
|
|
* producer's check_exported_type may reject a dangling export. */
|
|
if (wwiout) {
|
|
FILE *wf = fopen(wwiout, "wb");
|
|
if (wf == NULL) {
|
|
fprintf(stderr, "w6c: cannot open %s\n", wwiout);
|
|
return 1;
|
|
}
|
|
if (wwi_emit(&c, wf, file) != 0) { fclose(wf); return 1; }
|
|
fclose(wf);
|
|
}
|
|
|
|
FILE *of = stdout;
|
|
if (out) {
|
|
of = fopen(out, "wb");
|
|
if (of == NULL) {
|
|
fprintf(stderr, "w6c: cannot open %s\n", out);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
cg_init(&cg, a);
|
|
cg.sep_mode = sepmode;
|
|
/* Export production and entry identity are independent package-action
|
|
* properties. Legacy raw invocations without -I remain entry-like; every
|
|
* driver package now supplies -I, and only link roots add --entry. */
|
|
cg.sep_isdep = (wwiout != NULL && !entrymode);
|
|
cg_file(&cg, of, file);
|
|
|
|
if (of != stdout) fclose(of);
|
|
freearena(a);
|
|
for (int i = 0; i < nimports; i++) free(imports[i].buf);
|
|
free(imports);
|
|
free(buf);
|
|
return 0;
|
|
}
|