/* * 738_module_decl — sentinel for the `package ;` keyword. * * Pins three invariants from the module-system rewrite: * 1. The parser ACCEPTS `package foo;` as the first non-comment item. * 2. The parser ACCEPTS multiple `package X;` decls (concatenated * multi-file streams from the driver) and stamps subsequent * decls with whichever package is "current". * 3. Bare comments + a `package foo;` is still legal — the keyword * may follow leading comments. * * The relaxed-or-strict missing-package check was softened to a * silent default (curmod=NULL) so legacy fragment-driven tests * still parse. That softening is documented at parsefile() in * cmd/wcc/parse.c. */ #include "ww.h" #include #include #include static int parses_clean(const char *src) { Arena *a = newarena(); Lex l; Parser p; lexinit(&l, a, "", src, strlen(src)); parserinit(&p, a, &l); Node *n = parsefile(&p); int ok = (n != NULL && p.errs == 0 && l.errs == 0); freearena(a); return ok; } static const char *first_decl_module(Node *file) { if (file == NULL || file->list == NULL) return NULL; return file->list->module; } static int check_module_stamps(const char *src, const char *want_first, const char *want_last) { Arena *a = newarena(); Lex l; Parser p; lexinit(&l, a, "", src, strlen(src)); parserinit(&p, a, &l); Node *n = parsefile(&p); int ok = (n != NULL && p.errs == 0 && l.errs == 0); if (ok) { const char *first = first_decl_module(n); Node *last = n->list; while (last && last->next) last = last->next; const char *lastmod = last ? last->module : NULL; int match_first = (first == NULL && want_first == NULL) || (first != NULL && want_first != NULL && strcmp(first, want_first) == 0); int match_last = (lastmod == NULL && want_last == NULL) || (lastmod != NULL && want_last != NULL && strcmp(lastmod, want_last) == 0); ok = match_first && match_last; } freearena(a); return ok; } int main(void) { int pass = 0, fail = 0; /* Row 1: `package foo;` accepted as first non-comment item. */ if (parses_clean("package foo;\nfn x() void = {};\n")) pass++; else { fprintf(stderr, "738[1] basic package accept FAILED\n"); fail++; } /* Row 2: comments before `package foo;` legal. */ if (parses_clean("// header\n// more\npackage foo;\nfn x() void = {};\n")) pass++; else { fprintf(stderr, "738[2] package after comments FAILED\n"); fail++; } /* Row 3: subsequent decls stamped with current package. */ if (check_module_stamps( "package foo;\nfn x() void = {};\nfn y() void = {};\n", "foo", "foo")) pass++; else { fprintf(stderr, "738[3] decl module stamping FAILED\n"); fail++; } /* Row 4: concatenated multi-section stream — second package * decl switches the stamp for subsequent decls. Mirrors driver- * emitted multi-file modules. */ if (check_module_stamps( "package foo;\nfn a() void = {};\n" "package bar;\nfn b() void = {};\n", "foo", "bar")) pass++; else { fprintf(stderr, "738[4] mid-stream package switch FAILED\n"); fail++; } /* Row 5: dotted import accepted with leaf stored on N_USE. */ if (parses_clean( "package foo;\nimport encoding.utf8;\nfn x() void = {};\n")) pass++; else { fprintf(stderr, "738[5] dotted import accept FAILED\n"); fail++; } /* Row 6: dotted import stores only the leaf identifier on * N_USE.str (post-task-#22 — the driver translates the full * dotted path to a directory walk; the checker only needs the * package bareword for n_use → decl disambiguation). */ { Arena *a = newarena(); Lex l; Parser p; const char *src = "package foo;\nimport encoding.utf8;\n"; lexinit(&l, a, "", src, strlen(src)); parserinit(&p, a, &l); Node *n = parsefile(&p); int ok = 0; if (n != NULL && p.errs == 0 && l.errs == 0 && n->list != NULL) { Node *u = n->list; ok = (u->kind == N_USE && u->str != NULL && strcmp(u->str, "utf8") == 0); } freearena(a); if (ok) pass++; else { fprintf(stderr, "738[6] dotted import leaf-store FAILED\n"); fail++; } } printf("738_module_decl: %d pass, %d fail\n", pass, fail); return fail == 0 ? 0 : 1; }