compiler: load imports and enforce package exports
This commit is contained in:
180
cmd/wcc/parse.c
180
cmd/wcc/parse.c
@@ -100,6 +100,59 @@ static Node *parsetype(Parser *p);
|
||||
static Node *parseblock(Parser *p);
|
||||
static Node *parsestmt(Parser *p);
|
||||
|
||||
static void
|
||||
skipdecl(Parser *p)
|
||||
{
|
||||
int paren = 0, bracket = 0, brace = 0;
|
||||
while (p->cur.kind != TK_EOF) {
|
||||
switch (p->cur.kind) {
|
||||
case TK_LPAREN: paren++; break;
|
||||
case TK_RPAREN: if (paren > 0) paren--; break;
|
||||
case TK_LBRACK: bracket++; break;
|
||||
case TK_RBRACK: if (bracket > 0) bracket--; break;
|
||||
case TK_LBRACE: brace++; break;
|
||||
case TK_RBRACE: if (brace > 0) brace--; break;
|
||||
case TK_SEMI:
|
||||
advance(p);
|
||||
if (paren == 0 && bracket == 0 && brace == 0)
|
||||
return;
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
advance(p);
|
||||
}
|
||||
}
|
||||
|
||||
/* Consume attribute syntax without parsing its expressions or any following
|
||||
* declaration. The imports-only pass needs only to recognize that the next
|
||||
* declaration is an attributed import, which full parsing rejects. */
|
||||
static void
|
||||
skipimportattrs(Parser *p)
|
||||
{
|
||||
while (p->cur.kind == TK_AT) {
|
||||
advance(p);
|
||||
if (p->cur.kind == TK_IDENT)
|
||||
advance(p);
|
||||
else {
|
||||
errorf(p->cur.pos, "expected identifier, got %s",
|
||||
tokname(p->cur.kind));
|
||||
p->errs++;
|
||||
}
|
||||
if (!accept(p, TK_LPAREN)) continue;
|
||||
int depth = 1;
|
||||
while (p->cur.kind != TK_EOF && depth > 0) {
|
||||
if (p->cur.kind == TK_LPAREN) depth++;
|
||||
else if (p->cur.kind == TK_RPAREN) depth--;
|
||||
advance(p);
|
||||
}
|
||||
if (depth > 0) {
|
||||
errorf(p->cur.pos, "expected ')' after attribute");
|
||||
p->errs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Node *
|
||||
parseparams(Parser *p)
|
||||
{
|
||||
@@ -1277,25 +1330,134 @@ parseuse(Parser *p)
|
||||
/* M1 #22: accumulate the full dotted import path (n->module) so the
|
||||
* checker can match decl identity on the path, while n->str stays the
|
||||
* leaf alias the user writes (`utf8.x`). */
|
||||
char pathbuf[256];
|
||||
size_t pl = 0;
|
||||
const char *leaf = expectident(p);
|
||||
for (size_t i = 0; leaf[i] && pl + 1 < sizeof pathbuf; i++)
|
||||
pathbuf[pl++] = leaf[i];
|
||||
const char *path = leaf;
|
||||
while (accept(p, TK_DOT)) {
|
||||
leaf = expectident(p);
|
||||
if (pl + 1 < sizeof pathbuf) pathbuf[pl++] = '.';
|
||||
for (size_t i = 0; leaf[i] && pl + 1 < sizeof pathbuf; i++)
|
||||
pathbuf[pl++] = leaf[i];
|
||||
path = aprintf(p->a, "%s.%s", path, leaf);
|
||||
}
|
||||
pathbuf[pl] = '\0';
|
||||
n->str = leaf;
|
||||
n->strlen = strlen(leaf);
|
||||
n->usepath = astrndup(p->a, pathbuf, pl);
|
||||
n->usepath = path;
|
||||
expect(p, TK_SEMI);
|
||||
return n;
|
||||
}
|
||||
|
||||
Node *
|
||||
parseimports(Parser *p)
|
||||
{
|
||||
Pos fp = { p->l->file, 1, 1 };
|
||||
Node *file = newnode(p->a, N_FILE, fp);
|
||||
Node *head = NULL, *tail = NULL;
|
||||
Node *packages = NULL, *packagetail = NULL;
|
||||
int sawpackage = 0;
|
||||
|
||||
while (p->cur.kind != TK_EOF) {
|
||||
/* Compiler/driver bundle markers carry package identity out of
|
||||
* band. They are not source declarations, so keep scanning for
|
||||
* the following package clause and imports. */
|
||||
if (p->cur.kind == TK_MODPATH) {
|
||||
sawpackage = 0;
|
||||
p->pathmod = p->cur.text;
|
||||
p->curmod = p->cur.text;
|
||||
p->resetmod = NULL;
|
||||
advance(p);
|
||||
continue;
|
||||
}
|
||||
if (p->cur.kind == TK_MODRESET) {
|
||||
const char *rp = p->cur.text;
|
||||
sawpackage = 0;
|
||||
advance(p);
|
||||
p->pathmod = NULL;
|
||||
p->curmod = rp;
|
||||
p->resetmod = rp;
|
||||
continue;
|
||||
}
|
||||
if (p->cur.kind == TK_MODULE) {
|
||||
Pos pp = p->cur.pos;
|
||||
advance(p);
|
||||
if (p->cur.kind != TK_IDENT) {
|
||||
errorf(p->cur.pos, "invalid or missing package clause");
|
||||
p->errs++;
|
||||
sawpackage = 1;
|
||||
skipdecl(p);
|
||||
continue;
|
||||
}
|
||||
const char *name = expectident(p);
|
||||
expect(p, TK_SEMI);
|
||||
p->curmod = name;
|
||||
Node *package = newnode(p->a, N_FILE, pp);
|
||||
package->module = name;
|
||||
if (packages == NULL)
|
||||
packages = package;
|
||||
else
|
||||
packagetail->next = package;
|
||||
packagetail = package;
|
||||
if (!sawpackage) {
|
||||
file->module = name;
|
||||
file->pos = pp;
|
||||
sawpackage = 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!sawpackage && p->pathmod == NULL && p->resetmod == NULL) {
|
||||
errorf(p->cur.pos, "invalid or missing package clause");
|
||||
p->errs++;
|
||||
sawpackage = 1;
|
||||
}
|
||||
if (p->cur.kind == TK_USE) {
|
||||
Node *d = parseuse(p);
|
||||
d->module = p->curmod;
|
||||
if (head == NULL)
|
||||
head = d;
|
||||
else
|
||||
tail->next = d;
|
||||
tail = d;
|
||||
continue;
|
||||
}
|
||||
if (p->cur.kind == TK_AT) {
|
||||
skipimportattrs(p);
|
||||
if (p->cur.kind == TK_EXPORT) advance(p);
|
||||
if (p->cur.kind == TK_USE) {
|
||||
errorf(p->cur.pos,
|
||||
"import cannot be exported or attributed");
|
||||
p->errs++;
|
||||
Node *d = parseuse(p);
|
||||
d->module = p->curmod;
|
||||
if (head == NULL)
|
||||
head = d;
|
||||
else
|
||||
tail->next = d;
|
||||
tail = d;
|
||||
continue;
|
||||
}
|
||||
skipdecl(p);
|
||||
continue;
|
||||
}
|
||||
if (p->cur.kind == TK_EXPORT && peek(p).kind == TK_USE) {
|
||||
advance(p);
|
||||
errorf(p->cur.pos,
|
||||
"import cannot be exported or attributed");
|
||||
p->errs++;
|
||||
Node *d = parseuse(p);
|
||||
d->module = p->curmod;
|
||||
if (head == NULL)
|
||||
head = d;
|
||||
else
|
||||
tail->next = d;
|
||||
tail = d;
|
||||
continue;
|
||||
}
|
||||
skipdecl(p);
|
||||
}
|
||||
file->list = head;
|
||||
/* The package-clause chain lets a directory loader validate every
|
||||
* selected file without inventing a second header grammar. It lives in
|
||||
* body because list is the public imports chain. */
|
||||
file->body = packages;
|
||||
return file;
|
||||
}
|
||||
|
||||
static Node *
|
||||
parsedef(Parser *p, int exp)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user