Repository navigation
feat: Support template literals in console.log
#3352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -201,6 +201,22 @@ function stringifyExpression(node: tinyest.Expression): string { | |
| return 'null'; | ||
| } | ||
|
|
||
| if (node[0] === NODE.templateLiteral) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The quasis are already cooked text, so a backtick or |
||
| const quasis = node[1]; | ||
| const expressions = node[2]; | ||
|
|
||
| let result = '`'; | ||
| quasis.forEach((quasi, i) => { | ||
| result += quasi; | ||
| if (i < expressions.length && expressions[i]) { | ||
| const expression = expressions[i]; | ||
| result += '${' + stringifyExpression(expression) + '}'; | ||
| } | ||
| }); | ||
|
|
||
| return result + '`'; | ||
| } | ||
|
|
||
| assertExhaustive(node); | ||
| } | ||
|
|
||
|
|
@@ -228,7 +244,8 @@ function isExpression(node: tinyest.AnyNode): node is tinyest.Expression { | |
| node[0] === NODE.postUpdate || | ||
| node[0] === NODE.objectExpr || | ||
| node[0] === NODE.conditionalExpr || | ||
| node[0] === NODE.nullLiteral | ||
| node[0] === NODE.nullLiteral || | ||
| node[0] === NODE.templateLiteral | ||
| ) { | ||
| node satisfies tinyest.Expression; | ||
| return true; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -144,3 +144,28 @@ export class ArrayExpression implements SelfResolvable, WithComptimeValue { | |
| return ctx.gen.typeInstantiation(this.type, this.elements); | ||
| } | ||
| } | ||
|
|
||
| export class TemplateLiteralExpression implements WithComptimeValue { | ||
| readonly [$internal] = true; | ||
| readonly elements: Snippet[]; | ||
|
|
||
| constructor(elements: Snippet[]) { | ||
| this.elements = elements; | ||
| } | ||
|
|
||
| toString(): string { | ||
| return 'TemplateLiteralExpression'; | ||
| } | ||
|
|
||
| [$comptimeValueOf](): ComptimeValue { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| const values: unknown[] = []; | ||
| for (const elem of this.elements) { | ||
| const known = asComptime(elem); | ||
| if (!known) { | ||
| return { comptime: false }; | ||
| } | ||
| values.push(known.value); | ||
| } | ||
| return { comptime: true, value: values }; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,6 +46,7 @@ import { | |
| coerceToSnippet, | ||
| concretize, | ||
| numericLiteralToSnippet, | ||
| TemplateLiteralExpression, | ||
| } from './generationHelpers.ts'; | ||
| import { Swizzle } from './swizzle.ts'; | ||
| import { accessIndex } from './accessIndex.ts'; | ||
|
|
@@ -523,7 +524,12 @@ export class WgslGenerator implements ShaderGenerator { | |
| const callee = mathToStd.get(_callee) ?? _callee; | ||
|
|
||
| if (supportedLogOps().includes(callee)) { | ||
| return this.ctx.generateLog(callee, args); | ||
| return this.ctx.generateLog( | ||
| callee, | ||
| args.flatMap((arg) => | ||
| arg.value instanceof TemplateLiteralExpression ? arg.value.elements : [arg], | ||
| ), | ||
|
Comment on lines
+529
to
+531
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This only flattens one level. A nested template literal such as |
||
| ); | ||
| } | ||
|
|
||
| if (callee === constant) { | ||
|
|
@@ -1180,6 +1186,31 @@ The generated shader will evaluate them in declaration order.`, | |
| return snip(expression[1], UnknownData, /* origin */ 'constant', false); | ||
| } | ||
|
|
||
| if (expression[0] === NODE.templateLiteral) { | ||
| const [_, quasis, expressions] = expression; | ||
| const expressionsSnippets = expressions.map((expr) => this._expression(expr)); | ||
| const allConstant = expressionsSnippets.every((value) => value.origin === 'constant'); | ||
|
|
||
| const quasisSnippets = quasis.map((quasi) => | ||
| snip(quasi, UnknownData, /* origin */ 'constant', false), | ||
| ); | ||
|
|
||
| const values = quasisSnippets.flatMap((quasi, i) => { | ||
| const expr = expressionsSnippets[i]; | ||
| if (expr) { | ||
| return [quasi, expr]; | ||
| } | ||
| return [quasi]; | ||
| }); | ||
|
|
||
| return snip( | ||
| new TemplateLiteralExpression(values), | ||
| UnknownData, | ||
| /* origin */ allConstant ? 'constant' : 'runtime', | ||
| values.some((v) => v.possibleSideEffects), | ||
| ); | ||
| } | ||
|
|
||
| if (expression[0] === NODE.preUpdate) { | ||
| throw new Error('Cannot use pre-updates in TypeGPU functions.'); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fallback is unreachable for the supported syntax:
cookedis onlynullfor tagged templates with invalid escapes, andTaggedTemplateExpressionis not handled by the transpiler. If it is ever reached, it is also wrong:\is cooked before\\\\, so\becomes\+ newline, and the final.replaceAll(/\\/g, '\\')is a no-op. Consider removing the fallback or implementing a proper single-pass cook.