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
3 changes: 1 addition & 2 deletions packages/tinyest-for-wgsl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,12 @@
"prepublishOnly": "tgpu-dev-cli prepack"
},
"dependencies": {
"@babel/types": "catalog:",
"tinyest": "workspace:~"
},
"devDependencies": {
"@babel/parser": "^7.27.0",
"@babel/types": "catalog:",
"@typegpu/tgpu-dev-cli": "workspace:*",
"acorn": "^8.14.1",
"tsdown": "catalog:build",
"typescript": "catalog:types"
},
Expand Down
7 changes: 3 additions & 4 deletions packages/tinyest-for-wgsl/src/externals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Context, JsNode } from './types.ts';
import type * as babel from '@babel/types';
import type { Context } from './types.ts';

function isDeclared(ctx: Context, name: string) {
return ctx.stack.some((scope) => scope.declaredNames.includes(name));
Expand All @@ -13,7 +14,7 @@ function isDeclared(ctx: Context, name: string) {
* tryFindExternalChain(ctx, node`local.p.q`); // undefined
* tryFindExternalChain(ctx, node`ext.$.q`); // undefined
*/
export function tryFindExternalChain(ctx: Context, node: JsNode): string | undefined {
export function tryFindExternalChain(ctx: Context, node: babel.Node): string | undefined {
if (node.type === 'Identifier' && !isDeclared(ctx, node.name)) {
return node.name;
}
Expand All @@ -31,8 +32,6 @@ export function tryFindExternalChain(ctx: Context, node: JsNode): string | undef
property = node.property.name;
} else if (node.property.type === 'PrivateName') {
property = `#${node.property.id.name}`;
} else if (node.property.type === 'PrivateIdentifier') {
property = `#${node.property.name}`;
} else {
return;
}
Expand Down
91 changes: 37 additions & 54 deletions packages/tinyest-for-wgsl/src/parsers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type * as babel from '@babel/types';
import type * as acorn from 'acorn';
import * as tinyest from 'tinyest';
import { FuncParameterType } from 'tinyest';
import type { Context, JsNode, TranspilationResult } from './types.ts';
import type { Context, TranspilationResult } from './types.ts';
import { tryFindExternalChain } from './externals.ts';

const { NodeTypeCatalog: NODE } = tinyest;
Expand All @@ -12,9 +11,9 @@ const tsFallthrough = (ctx: Context, node: { expression: babel.Expression }): ti
};

const Transpilers: Partial<{
[Type in JsNode['type']]: (
[Type in babel.Node['type']]: (
ctx: Context,
node: Extract<JsNode, { type: Type }>,
node: Extract<babel.Node, { type: Type }>,
) => tinyest.AnyNode;
}> = {
Program(ctx, node) {
Expand Down Expand Up @@ -51,7 +50,7 @@ const Transpilers: Partial<{
? [NODE.return, transpile(ctx, node.argument) as tinyest.Expression]
: [NODE.return],

Identifier(ctx, node) {
Identifier(_ctx, node) {
return node.name;
},

Expand Down Expand Up @@ -121,36 +120,20 @@ const Transpilers: Partial<{
return [NODE.conditionalExpr, test, consequent, alternative];
},

Literal(ctx, node) {
if (typeof node.value === 'boolean') {
return node.value;
}
if (typeof node.value === 'string') {
return [NODE.stringLiteral, node.value];
}
if (node.regex) {
throw new Error('Regular expression literals are not representable in WGSL.');
}
if (node.bigint) {
console.warn('BigInt literals are represented as numbers - loss of precision may occur.');
}
return [NODE.numericLiteral, String(Number(node.value))];
},

NumericLiteral(ctx, node) {
NumericLiteral(_ctx, node) {
return [NODE.numericLiteral, String(node.value)];
},

BigIntLiteral(ctx, node) {
BigIntLiteral(_ctx, node) {
console.warn('BigInt literals are represented as numbers - loss of precision may occur.');
return [NODE.numericLiteral, String(Number.parseInt(node.value))];
},

BooleanLiteral(ctx, node) {
BooleanLiteral(_ctx, node) {
return node.value;
},

StringLiteral(ctx, node) {
StringLiteral(_ctx, node) {
return [NODE.stringLiteral, node.value];
},

Expand Down Expand Up @@ -220,22 +203,33 @@ const Transpilers: Partial<{
throw new Error('Spread elements are not supported in TGSL.');
}

// TODO: Handle computed properties
if (prop.key.type !== 'Identifier' && prop.key.type !== 'Literal') {
throw new Error('Only Identifier and Literal keys are supported as object keys.');
}

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

ctx.ignoreExternalDepth++;
const key =
prop.key.type === 'Identifier'
? (transpile(ctx, prop.key) as string)
: String(prop.key.value);
ctx.ignoreExternalDepth--;
// TODO: Handle computed properties
if (prop.computed) {
Comment thread
cieplypolar marked this conversation as resolved.
throw new Error('Computed object properties are not supported in TGSL.');
}

let key: string;

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

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

default:
throw new Error(`Unsupported non-computed object property key: ${prop.key.type}`);
}

const value = transpile(ctx, prop.value) as tinyest.Expression;

properties[key] = value;
Expand Down Expand Up @@ -289,7 +283,7 @@ const Transpilers: Partial<{
TSNonNullExpression: tsFallthrough,
};

function transpile(ctx: Context, node: JsNode): tinyest.AnyNode {
function transpile(ctx: Context, node: babel.Node): tinyest.AnyNode {
const transpiler = Transpilers[node.type];

if (!transpiler) {
Expand All @@ -310,15 +304,11 @@ function transpile(ctx: Context, node: JsNode): tinyest.AnyNode {
return transpiler(ctx, node);
}

export function extractFunctionParts(rootNode: JsNode): {
export function extractFunctionParts(rootNode: babel.Node): {
params: tinyest.FuncParameter[];
body: acorn.BlockStatement | acorn.Expression | babel.BlockStatement | babel.Expression;
body: babel.BlockStatement | babel.Expression;
} {
let functionNode:
| acorn.ArrowFunctionExpression
| acorn.FunctionExpression
| acorn.FunctionDeclaration
| acorn.AnonymousFunctionDeclaration
| babel.ArrowFunctionExpression
| babel.FunctionExpression
| babel.FunctionDeclaration
Expand Down Expand Up @@ -380,19 +370,12 @@ export function extractFunctionParts(rootNode: JsNode): {
}

return {
params: (
functionNode.params as (
| babel.Identifier
| acorn.Identifier
| babel.ObjectPattern
| acorn.ObjectPattern
)[]
).map((param) =>
params: (functionNode.params as (babel.Identifier | babel.ObjectPattern)[]).map((param) =>
param.type === 'ObjectPattern'
? {
type: FuncParameterType.destructuredObject,
props: param.properties.flatMap((prop) =>
(prop.type === 'Property' || prop.type === 'ObjectProperty') &&
prop.type === 'ObjectProperty' &&
prop.key.type === 'Identifier' &&
prop.value.type === 'Identifier'
? [{ name: prop.key.name, alias: prop.value.name }]
Expand All @@ -408,7 +391,7 @@ export function extractFunctionParts(rootNode: JsNode): {
};
}

export function transpileFn(rootNode: JsNode): TranspilationResult {
export function transpileFn(rootNode: babel.Node): TranspilationResult {
const { params, body } = extractFunctionParts(rootNode);

const ctx: Context = {
Expand Down Expand Up @@ -443,7 +426,7 @@ export function transpileFn(rootNode: JsNode): TranspilationResult {
};
}

export function transpileNode(node: JsNode): tinyest.AnyNode {
export function transpileNode(node: babel.Node): tinyest.AnyNode {
const ctx: Context = {
externalNames: new Map(),
ignoreExternalDepth: 0,
Expand Down
5 changes: 1 addition & 4 deletions packages/tinyest-for-wgsl/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type * as babel from '@babel/types';
import type * as acorn from 'acorn';
import * as tinyest from 'tinyest';

export type Scope = {
Expand All @@ -20,7 +19,7 @@ export type Context = {
* instead of traversing chains `.x.y.z.t`, `.x.y.z`, `.x.y` and `.x`,
* we only traverse the first one and then return early.
*/
visitedNodes: Set<babel.MemberExpression | acorn.MemberExpression>;
visitedNodes: Set<babel.MemberExpression>;
stack: Scope[];
};

Expand All @@ -33,5 +32,3 @@ export type TranspilationResult = {
*/
externalNames: Externals;
};

export type JsNode = babel.Node | acorn.AnyNode;
9 changes: 0 additions & 9 deletions packages/tinyest-for-wgsl/tests/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
import babel from '@babel/parser';
import type { Node } from '@babel/types';
import * as acorn from 'acorn';

export const parseRollup = (code: string) => acorn.parse(code, { ecmaVersion: 'latest' });
export const parseBabel = (code: string) =>
babel.parse(code, { sourceType: 'module', plugins: ['typescript'] }).program.body[0] as Node;

export function dualTest(test: (p: (code: string) => Node | acorn.AnyNode) => void) {
return () => {
test(parseBabel);
test(parseRollup);
};
}
Loading
Loading