Files
ww/test/wcc/200_parse.c
Hojun-Cho 2c33228b7e ww: rename toolchain to w-prefix + hare-style build/run/test driver
Plan 9-style w-prefix on the per-arch tools, disambiguating from the
real Plan 9 6c/6a/6l in ref/plan9front/:

    cmd/wwc/      → cmd/wcc/        libwwc.a → libwcc.a
    cmd/6{c,a,l}  → cmd/w6{c,a,l}   binary names too
    test/wwc/     → test/wcc/       6 test files w/ w6 prefix
    selfhost/cmd  mirror in lockstep
    bootstrap/amd64/{w6c,w6a,w6l}   snapshot binaries (gitignored)
    WW_6{C,A,L}   → WW_W6{C,A,L}    env-var overrides

Plan 9 source-tree refs ("Plan 9 6c shape", ref/plan9front/, etc.)
preserved. Hare-style driver, both C and ww sides:

    ww test [path]   discover *_test.ww in a directory module, run
                     each; single-file mode for `ww test foo.ww`
    Module-by-name   `ww build foo` resolves to foo.ww or foo/foo.ww
                     via search path (cwd : -I dirs : $WW_LIB)
    Default-to-cwd   `ww build` / `ww test` build the cwd module
    Run pass-through `ww run path arg1 arg2` reaches the program

lib/os: getcwd (79) and getdents64 (217) syscalls power `.` resolution
and directory enumeration on the ww side.

Makefile: wwstage tool deps now include lib/os/os.ww (+ lib/strconv
for wwdump_ww) so lib/* edits force their rebuild instead of leaving
stale binaries — surfaced when test 995 first failed against a stale
w6c_ww built before the lib/os additions.

Test 993 byte-identical parity gate (C-side ww vs ww-side ww_ww on a
build corpus) stays green; all 19 tests pass.
2026-05-11 13:49:27 +09:00

191 lines
5.9 KiB
C

/*
* 200_parse — parser tests.
*
* Two flavours:
* 1) "must parse": source must produce a Node and no errors.
* 2) "shape match": parsed AST printed via astprint, compared against
* expected substring (so tests stay readable without binding to
* every position field).
*/
#include "ww.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static int
must_parse(const char *src)
{
Arena *a = newarena();
Lex l;
Parser p;
lexinit(&l, a, "<test>", 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 char *
parse_to_str(const char *src, int *errs)
{
Arena *a = newarena();
Lex l;
Parser p;
lexinit(&l, a, "<test>", src, strlen(src));
parserinit(&p, a, &l);
Node *n = parsefile(&p);
*errs = p.errs + l.errs;
char *buf = NULL;
size_t len = 0;
FILE *f = open_memstream(&buf, &len);
if (f == NULL) {
freearena(a);
return NULL;
}
astprint(f, n);
fclose(f);
char *out = malloc(len + 1);
memcpy(out, buf, len);
out[len] = '\0';
free(buf);
freearena(a);
return out;
}
static int
must_contain(const char *src, const char *needle)
{
int errs;
char *got = parse_to_str(src, &errs);
if (got == NULL || errs > 0) {
fprintf(stderr, "parse errs=%d:\n%s\n", errs, src);
free(got);
return 0;
}
if (strstr(got, needle) == NULL) {
fprintf(stderr, "shape mismatch:\n src: %s\n"
" want: %s\n got:\n%s\n", src, needle, got);
free(got);
return 0;
}
free(got);
return 1;
}
int
main(void)
{
int fail = 0;
const char *parses[] = {
"use io;",
"use io.bufio;",
"def MAX: i32 = 4096;",
"export def MAX: i32 = 4096;",
"type point = struct { x: i32, y: i32 };",
"type stream = fn(b: []u8) i32;",
"type chans = chan i32;",
"type box = struct { p: *point, n: i32, items: []i32 };",
"type arr = [16]u8;",
"fn nop() void = {};",
"export fn id(x: i32) i32 = { return x; };",
"fn add(a: i32, b: i32) i32 = { return a + b; };",
"@symbol(\"malloc\") fn cmalloc(n: u64) *void;",
"fn varadic(a: i32, ...) void;",
"fn f() void = { let x: i32 = 0; let y = 1.5; let s: str = \"hi\"; };",
"fn f() void = { if (x > 0) { return; } else { x += 1; }; };",
"fn f() void = { for (let i: i32 = 0; i < 10; i += 1) { x += i; }; };",
"fn f() void = { for (i < 10) { i += 1; }; };",
"fn f() void = { for () { i += 1; }; };",
"fn f() void = { defer free(p); };",
"fn f() void = { switch (x) { case 1, 2: y = 1; case: y = 0; }; };",
"fn ptr(p: *point) i32 = { return p.x; };",
"fn cast() void = { let x = (5 + 1): i64; };",
"fn deref(p: *i32) i32 = { return *p; };",
"fn addr(x: i32) *i32 = { return &x; };",
"fn lit() void = { let p: point = point { x = 1, y = 2 }; };",
"fn pkg() void = { fmt.println(1); };",
"fn arr() void = { let a = [1, 2, 3]; };",
"fn idx() i32 = { return a[3]; };",
"fn neg() i32 = { return -a + ~b * !c; };",
"fn ops() void = { x = 1; x += 1; x -= 1; x *= 2; x /= 2; x %= 2; "
"x &= 1; x |= 1; x ^= 1; x <<= 1; x >>= 1; };",
"fn cmp() bool = { return a == b && c != d || e < f && g <= h; };",
"fn bits() i32 = { return (a & b) | (c ^ d); };",
"fn shift() i32 = { return a << 2 | b >> 1; };",
"fn nested() void = { if (a > 0) { if (b > 0) { c = 1; }; }; };",
"fn many(a: i32, b: i32, c: i32, d: i32, e: i32) i32 = "
"{ return a + b * c - d / e; };",
"fn slc(s: []u8) []u8 = { return s; };",
"fn ssn(p: **point) i32 = { return (*p).x; };",
"fn arrptr(p: *[16]u8) u8 = { return p[0]; };",
"fn fnt(f: fn(i32) i32, x: i32) i32 = { return f(x); };",
"fn anontype() void = { let f: fn(i32) i32 = id; };",
/* multiple decls */
"use io;\nuse fmt;\ndef N: i32 = 8;\ntype p = struct{x:i32};\nfn f() void = {};",
/* attribute on FFI decl */
"@symbol(\"strlen\") fn cstrlen(s: *u8) u64;",
/* trailing comma */
"fn f(a: i32, b: i32,) void = {};",
/* nil/true/false */
"fn f() void = { let p: *i32 = nil; let x: bool = true; let y: bool = false; };",
/* chan */
"fn ch() void = { let c: chan i32; let v = <-c; };",
/* nested struct lit */
"fn lit2() void = { let q = box { p = nil, n = 0, items = [1, 2] }; };",
/* defer with call */
"fn f() void = { defer close(fd); return; };",
/* break / continue */
"fn f() void = { for () { if (x) { break; }; continue; }; };",
/* deeply nested expr */
"fn deep() i32 = { return ((((((1 + 2) * 3) - 4) / 5) % 6) << 7); };",
/* index chain */
"fn ix() i32 = { return a[b][c[d]]; };",
/* dot chain */
"fn dot() i32 = { return a.b.c.d; };",
/* call chain */
"fn cc() i32 = { return f()()(); };",
/* mixed postfix */
"fn mx() i32 = { return obj.method(arg)[idx].field; };",
/* multi-return tuples */
"fn divmod(a: i64, b: i64) (i64, i64) = { return a / b, a % b; };",
"fn try() (i32, str) = { return 0, \"\"; };",
"fn use_tuple() void = { let q, r = divmod(10, 3); };",
"fn assign_tuple() void = { q, r = divmod(10, 3); };",
};
int n = sizeof parses / sizeof parses[0];
for (int i = 0; i < n; i++) {
if (!must_parse(parses[i])) {
fprintf(stderr, "parse fail [%d]: %s\n", i, parses[i]);
fail++;
}
}
if (!must_contain("use io;", "(use \"io\"")) fail++;
if (!must_contain("def N: i32 = 4;", "(def \"N\"")) fail++;
if (!must_contain("def N: i32 = 4;", "(int 4")) fail++;
if (!must_contain("export fn f() void = {};", "(fn \"f\" export")) fail++;
if (!must_contain("type p = struct { x: i32 };", "(typedecl \"p\"")) fail++;
if (!must_contain("fn f() i32 = { return 1; };", "(return")) fail++;
if (!must_contain("fn f() void = { x = 1; };", "(assign =")) fail++;
if (!must_contain("fn f() i32 = { return a + b; };", "(bin +")) fail++;
if (!must_contain("@symbol(\"x\") fn f() void;", "(attr \"symbol\""))fail++;
if (fail) {
fprintf(stderr, "%d parse tests failed\n", fail);
return 1;
}
printf("parse: %d/%d ok + 9 shape ok\n", n, n);
return 0;
}