Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ export async function setupScene(root: TgpuRoot, context: GPUCanvasContext) {
'use gpu';
const shape = getMorphingShape(p, time.$);
const floor = Shape({
dist: sdPlane(p, d.vec3f(0, 1, 0), 0),
color: std.mix(d.vec3f(1), d.vec3f(0.2), checkerBoard(std.mul(p.xz, 2))),
dist: sdPlane(p, d.vec3f(0, 1, 0), 0),
});

return shapeUnion(shape, floor);
Expand Down
2 changes: 1 addition & 1 deletion apps/typegpu-docs/src/examples/simple/vaporrave/scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ export async function setupScene(root: TgpuRoot, context: GPUCanvasContext) {
Ray,
)((p) => {
const floor = Ray({
dist: sdPlane(p, c.planeOrthonormal, c.PLANE_OFFSET),
color: floorPatternSlot.$(p.xz, floorAngleUniform.$),
dist: sdPlane(p, c.planeOrthonormal, c.PLANE_OFFSET),
});
const sphere = getSphere(p, sphereColorUniform.$.rgb, c.sphereCenter, sphereAngleUniform.$);

Expand Down
9 changes: 0 additions & 9 deletions packages/eslint-plugin/src/rules/noUnsupportedSyntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,6 @@ export const noUnsupportedSyntax = createRule({
report(node, `'new' expression`);
},

Property(node) {
if (!directives.getEnclosingTypegpuFunction()) {
return;
}
if (node.computed) {
report(node, 'computed property key');
}
},

SequenceExpression(node) {
if (!directives.getEnclosingTypegpuFunction()) {
return;
Expand Down
10 changes: 1 addition & 9 deletions packages/eslint-plugin/tests/rules/noUnsupportedSyntax.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('noUnsupportedSyntax', () => {
"const fn = () => { 'use gpu'; const x = Struct({ prop: 1}); }",
"const fn = () => { 'use gpu'; let x = 1; }",
"const cls = new (class { #priv = 1; fn = () => { 'use gpu'; const a = this.#priv; } } )()",
"const fn = () => { 'use gpu'; const obj = { [key]: 1 }; }",
],
invalid: [
{
Expand Down Expand Up @@ -202,15 +203,6 @@ describe('noUnsupportedSyntax', () => {
},
],
},
{
code: "const fn = () => { 'use gpu'; const obj = { [key]: 1 }; }",
errors: [
{
messageId: 'unexpected',
data: { snippet: '[key]: 1', syntax: 'computed property key' },
},
],
},
{
code: "const fn = () => { 'use gpu'; (a, b); }",
errors: [
Expand Down
54 changes: 20 additions & 34 deletions packages/tinyest-for-wgsl/src/parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ function createLegacyTraspilers() {
...acornTranspilers,

ObjectExpression(ctx, node, transpile) {
const properties: Record<string, tinyest.Expression> = {};

for (const prop of node.properties) {
const objectProperties = node.properties.map((prop) => {
if (prop.type === 'SpreadElement') {
throw new Error('Spread elements are not supported in TGSL.');
}
Expand All @@ -42,41 +40,29 @@ function createLegacyTraspilers() {
throw new Error('Object method elements are not supported in TGSL.');
}

if (prop.computed) {
throw new Error('Computed object properties are not supported in TGSL.');
}
return transpile(ctx, prop) as tinyest.ObjectProperty;
});

let key: string;

switch (prop.key.type) {
// Shared
case 'Identifier':
key = prop.key.name;
break;

// Babel
case 'StringLiteral':
case 'NumericLiteral':
case 'BigIntLiteral':
key = String(prop.key.value);
break;

// Acorn
case 'Literal':
if (prop.key.raw !== null && !prop.key.regex) {
key = String(prop.key.value);
break;
}

default:
throw new Error(`Unsupported non-computed object property key.`);
}
if (objectProperties.some((prop) => /* computed */ prop[3])) {
return [
NODE.objectExprWithComputedProps,
objectProperties,
] as tinyest.ObjectExpressionWithComputedProps;
}

const obj: Record<string, tinyest.Expression> = {};
const seenKeys = new Set<string>();

const value = transpile(ctx, prop.value) as tinyest.Expression;
properties[key] = value;
for (const prop of objectProperties) {
const key = prop[1] as string;
if (seenKeys.has(key)) {
throw new Error(`Duplicate object property key: '${key}'.`);
}
seenKeys.add(key);
obj[key] = /* value */ prop[2];
}

return [NODE.objectExpr, properties];
return [NODE.objectExpr, obj] as tinyest.ObjectExpression;
},
} as Transpilers<JsNode>;
}
Expand Down
126 changes: 84 additions & 42 deletions packages/tinyest-for-wgsl/src/transpilers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,28 @@ const acornSpecificTranspilers = {
return [NODE.numericLiteral, String(Number(node.value))];
},

ObjectExpression(ctx, node, transpile) {
const properties: Record<string, tinyest.Expression> = {};
Property(ctx, node, transpile) {
if (node.computed) {
const key = transpile(ctx, node.key) as tinyest.Expression;
const value = transpile(ctx, node.value) as tinyest.Expression;

return [NODE.objectProperty, key, value, true] as tinyest.ObjectProperty;
}

if (
(node.key.type !== 'Identifier' && node.key.type !== 'Literal') ||
(node.key.type === 'Literal' && (node.key.raw === null || node.key.regex))
) {
throw new Error(`Unsupported non-computed object property key.`);
}

const key = node.key.type === 'Identifier' ? node.key.name : String(node.key.value);
const value = transpile(ctx, node.value) as tinyest.Expression;
return [NODE.objectProperty, key, value, false] as tinyest.ObjectProperty;
},

for (const prop of node.properties) {
ObjectExpression(ctx, node, transpile) {
const objectProperties = node.properties.map((prop) => {
// TODO: Handle SpreadElement
if (prop.type === 'SpreadElement') {
throw new Error('Spread elements are not supported in TGSL.');
Expand All @@ -254,24 +272,29 @@ const acornSpecificTranspilers = {
throw new Error('Object method elements are not supported in TGSL.');
}

// TODO: Handle computed properties
if (prop.computed) {
throw new Error('Computed object properties are not supported in TGSL.');
}
return transpile(ctx, prop) as tinyest.ObjectProperty;
});

if (
(prop.key.type !== 'Identifier' && prop.key.type !== 'Literal') ||
(prop.key.type === 'Literal' && (prop.key.raw === null || prop.key.regex))
) {
throw new Error(`Unsupported non-computed object property key.`);
}
if (objectProperties.some((prop) => /* computed */ prop[3])) {
return [
NODE.objectExprWithComputedProps,
objectProperties,
] as tinyest.ObjectExpressionWithComputedProps;
}

const key = prop.key.type === 'Identifier' ? prop.key.name : String(prop.key.value);
const value = transpile(ctx, prop.value) as tinyest.Expression;
properties[key] = value;
const obj: Record<string, tinyest.Expression> = {};
const seenKeys = new Set<string>();

for (const prop of objectProperties) {
const key = prop[1] as string;
if (seenKeys.has(key)) {
throw new Error(`Duplicate object property key: '${key}'.`);
}
seenKeys.add(key);
obj[key] = /* value */ prop[2];
}

return [NODE.objectExpr, properties];
return [NODE.objectExpr, obj] as tinyest.ObjectExpression;
},
} satisfies Transpilers<acorn.AnyNode>;

Expand Down Expand Up @@ -310,47 +333,66 @@ const babelSpecificTranspilers = {
return [NODE.nullLiteral];
},

ObjectExpression(ctx, node, transpile) {
const properties: Record<string, tinyest.Expression> = {};
ObjectProperty(ctx, node, transpile) {
Comment thread
cieplypolar marked this conversation as resolved.
if (node.computed) {
const key = transpile(ctx, node.key) as tinyest.Expression;
const value = transpile(ctx, node.value) as tinyest.Expression;

return [NODE.objectProperty, key, value, true] as tinyest.ObjectProperty;
}

let key: string;
switch (node.key.type) {
case 'Identifier':
key = node.key.name;
break;
case 'StringLiteral':
case 'NumericLiteral':
case 'BigIntLiteral':
key = String(node.key.value);
break;
default:
throw new Error(`Unsupported non-computed object property key.`);
}

const value = transpile(ctx, node.value) as tinyest.Expression;
return [NODE.objectProperty, key, value, false] as tinyest.ObjectProperty;
},

for (const prop of node.properties) {
ObjectExpression(ctx, node, transpile) {
const objectProperties = node.properties.map((prop) => {
// TODO: Handle SpreadElement
if (prop.type === 'SpreadElement') {
throw new Error('Spread elements are not supported in TGSL.');
}

// TODO: Handle Object method
if (prop.type === 'ObjectMethod') {
throw new Error('Object method elements are not supported in TGSL.');
}

// TODO: Handle computed properties
if (prop.computed) {
throw new Error('Computed object properties are not supported in TGSL.');
}

let key: string;
return transpile(ctx, prop) as tinyest.ObjectProperty;
});

switch (prop.key.type) {
case 'Identifier':
key = prop.key.name;
break;
if (objectProperties.some((prop) => /* computed */ prop[3])) {
return [
NODE.objectExprWithComputedProps,
objectProperties,
] as tinyest.ObjectExpressionWithComputedProps;
}

case 'StringLiteral':
case 'NumericLiteral':
case 'BigIntLiteral':
key = String(prop.key.value);
break;
const obj: Record<string, tinyest.Expression> = {};
const seenKeys = new Set<string>();

default:
throw new Error(`Unsupported non-computed object property key.`);
for (const prop of objectProperties) {
const key = prop[1] as string;
if (seenKeys.has(key)) {
throw new Error(`Duplicate object property key: '${key}'.`);
}

const value = transpile(ctx, prop.value) as tinyest.Expression;
properties[key] = value;
seenKeys.add(key);
obj[key] = /* value */ prop[2];
}

return [NODE.objectExpr, properties];
return [NODE.objectExpr, obj] as tinyest.ObjectExpression;
},

TSAsExpression: tsFallthrough,
Expand Down
70 changes: 59 additions & 11 deletions packages/tinyest-for-wgsl/tests/parsers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,18 +409,65 @@ describe('transpileFnBabel and transpileFnAcorn', () => {
it(
'parses binary bigints',
dualTest((p, transpileFn) => {
expect(JSON.stringify(transpileFn(p('() => 0b101n')).body)).toMatchInlineSnapshot(
`"[0,[[10,[5,"5"]]]]"`,
const { body, externalNames } = transpileFn(p('() => 0b101n'));

expect(JSON.stringify(body)).toMatchInlineSnapshot(`"[0,[[10,[5,"5"]]]]"`);
expect(externalNames).toMatchInlineSnapshot(`Map {}`);
}),
);

it(
'parses identifier, string, numeric, and bigint object keys',
dualTest((p, transpileFn) => {
const { body, externalNames } = transpileFn(
p(`() => ({
identifier: 1,
'string-key': 2,
1: 3,
2n: 4,
})`),
);

expect(JSON.stringify(body)).toMatchInlineSnapshot(
`"[0,[[10,[104,{"1":[5,"3"],"2":[5,"4"],"identifier":[5,"1"],"string-key":[5,"2"]}]]]]"`,
);
expect(externalNames).toMatchInlineSnapshot(`Map {}`);
}),
);

it(
'rejects computed object properties',
'rejects duplicate non-computed object keys',
dualTest((p, transpileFn) => {
expect(() => transpileFn(p('() => ({ [k]: 1 })'))).toThrowErrorMatchingInlineSnapshot(
`[Error: Computed object properties are not supported in TGSL.]`,
expect(() =>
transpileFn(
p(`() => ({
field: 1,
field: 2,
})`),
),
).toThrowErrorMatchingInlineSnapshot(`[Error: Duplicate object property key: 'field'.]`);
}),
);

it(
'parses computed object keys',
dualTest((p, transpileFn) => {
const { body, externalNames } = transpileFn(
p(`() => ({
[id]: 1,
[getId()]: 2,
})`),
);

expect(JSON.stringify(body)).toMatchInlineSnapshot(
`"[0,[[10,[108,[[107,"id",[5,"1"],true],[107,[6,"getId",[]],[5,"2"],true]]]]]]"`,
);
expect(externalNames).toMatchInlineSnapshot(`
Map {
"id" => "id",
"getId" => "getId",
}
`);
}),
);
});
Expand All @@ -445,16 +492,17 @@ describe('legacy transpileFn', () => {
);
});

it('rejects computed object properties', () => {
it('parses computed object properties', () => {
const code = `() => ({
[1]: 2,
[id]: 1,
[getId()]: 2,
});`;

expect(() => transpileFn(parseBabel(code))).toThrowErrorMatchingInlineSnapshot(
`[Error: Computed object properties are not supported in TGSL.]`,
expect(JSON.stringify(transpileFn(parseBabel(code)).body)).toMatchInlineSnapshot(
`"[0,[[10,[108,[[107,"id",[5,"1"],true],[107,[6,"getId",[]],[5,"2"],true]]]]]]"`,
);
expect(() => transpileFn(parseRollup(code))).toThrowErrorMatchingInlineSnapshot(
`[Error: Computed object properties are not supported in TGSL.]`,
expect(JSON.stringify(transpileFn(parseRollup(code)).body)).toMatchInlineSnapshot(
`"[0,[[10,[108,[[107,"id",[5,"1"],true],[107,[6,"getId",[]],[5,"2"],true]]]]]]"`,
);
});

Expand Down
Loading
Loading