import { _withBaseLogger, or, repeat } from "mini-parse"; import { expectNoLogErr, logCatch } from "mini-parse/test-util"; import { expect, test } from "vitest"; import { AbstractElem, FnElem, StructElem, VarElem } from "../AbstractElems.js"; import { filterElems } from "../ParseModule.js"; import { unknown, wordNumArgs } from "../ParseSupport.js"; import { fnDecl, globalVar, parseWgslD, structDecl, typeSpecifier, } from "../ParseWgslD.js"; import { testAppParse } from "./TestUtil.js"; function testParseWgsl(src: string): AbstractElem[] { return parseWgslD(src, undefined, {}, 500); } test("parse empty string", () => { const parsed = testParseWgsl(""); expect(parsed).toMatchSnapshot(); }); test("parse fn foo() { }", () => { const src = "fn foo() { }"; const parsed = testParseWgsl(src); expect(parsed).toMatchSnapshot(); }); test("parse fn with calls", () => { const src = "fn foo() { foo(); bar(); }"; const parsed = testParseWgsl(src); expect(parsed).toMatchSnapshot(); }); test("structDecl parses struct member types", () => { const src = "struct Foo { a: f32, b: i32 }"; const { appState } = testAppParse(structDecl, src); const { members } = appState[0] as StructElem; const typeNames = members.flatMap((m) => m.typeRefs.map((t) => t.name)); expect(typeNames).deep.eq(["f32", "i32"]); }); test("parse struct", () => { const src = "struct Foo { a: f32, b: i32 }"; const parsed = testParseWgsl(src); expect(parsed).toMatchInlineSnapshot(` [ { "end": 29, "kind": "struct", "members": [ { "end": 19, "kind": "member", "name": "a", "start": 13, "typeRefs": [ { "end": 19, "kind": "typeRef", "name": "f32", "start": 16, }, ], }, { "end": 27, "kind": "member", "name": "b", "start": 21, "typeRefs": [ { "end": 27, "kind": "typeRef", "name": "i32", "start": 24, }, ], }, ], "name": "Foo", "nameElem": { "end": 10, "kind": "typeName", "name": "Foo", "start": 7, }, "start": 0, }, ] `); }); test("parse @attribute before fn", () => { const src = ` @compute fn main() {} `; const parsed = testParseWgsl(src); expect(parsed).toMatchSnapshot(); }); test("wordNumArgs parses (a, b, 1)", () => { const src = `(a, b, 1)`; const { parsed } = testAppParse(wordNumArgs, src); expect(parsed?.value).toMatchSnapshot(); }); test("parse @compute @workgroup_size(a, b, 1) before fn", () => { const src = ` @compute @workgroup_size(a, b, 1) fn main() {} `; const parsed = testParseWgsl(src); expect(parsed).toMatchSnapshot(); }); test("parse global diagnostic", () => { const src = ` diagnostic(off,derivative_uniformity); fn main() {} `; expectNoLogErr(() => { const parsed = testParseWgsl(src); expect(parsed).toMatchSnapshot(); }); }); test("parse const_assert", () => { const src = ` const_assert x < y; fn main() {} `; expectNoLogErr(() => { const parsed = testParseWgsl(src); expect(parsed).toMatchSnapshot(); }); }); test("parse top level var", () => { const src = ` @group(0) @binding(0) var u: Uniforms; fn main() {} `; expectNoLogErr(() => { const parsed = testParseWgsl(src); expect(parsed).toMatchSnapshot(); }); }); test("parse top level override and const", () => { const src = ` override x = 21; const y = 1; fn main() {} `; expectNoLogErr(() => { const parsed = testParseWgsl(src); expect(parsed).toMatchSnapshot(); }); }); test("parse root level ;;", () => { const src = ";;"; expectNoLogErr(() => { const parsed = testParseWgsl(src); expect(parsed).toMatchSnapshot(); }); }); test("parse simple alias", () => { const src = `alias NewType = OldType;`; expectNoLogErr(() => { const parsed = testParseWgsl(src); expect(parsed).toMatchSnapshot(); }); }); test("parse array alias", () => { const src = ` alias Points3 = array; `; expectNoLogErr(() => { const parsed = testParseWgsl(src); expect(parsed).toMatchSnapshot(); }); }); test("unexpected token", () => { const p = repeat(or("a", unknown)); const { log, logged } = logCatch(); _withBaseLogger(log, () => testAppParse(p, "a b")); expect(logged()).toMatchInlineSnapshot(` "??? word: 'b' repeat > repeat > or > map a b Ln 1 ^" `); }); test("fnDecl parses fn with return type", () => { const src = ` fn foo() -> MyType { } `; const { appState } = testAppParse(fnDecl, src); expect((appState[0] as FnElem).typeRefs[0].name).eq("MyType"); }); test("fnDecl parses :type specifier in fn args", () => { const src = ` fn foo(a: MyType) { } `; const { appState } = testAppParse(fnDecl, src); const { typeRefs } = appState[0] as FnElem; expect(typeRefs[0].name).eq("MyType"); }); test("fnDecl parses :type specifier in fn block", () => { const src = ` fn foo() { var b:MyType; } `; const { appState } = testAppParse(fnDecl, src); expect((appState[0] as FnElem).typeRefs[0].name).eq("MyType"); }); test("parse type in