UNPKG

typegpu

Version:

A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.

94 lines (93 loc) 3.35 kB
import { blankSpaces, lineBreaks } from "./core/whitespaces.js"; import { invariant } from "./errors.js"; /** * We don't separate every WGSL token, for example `main()` already has no spaces, no need to further split it. */ const splitRegex = new RegExp(`[${[...blankSpaces].join('')}]+`, 'ug'); /** * Regex for detecting boundary characters that indicate whitespace may be needed between tokens. * @example * "let", "variable" // "t" and "v" both match, a space is needed * "return" "a" // "n" and "a" both match, a space is needed * "return" "(a+b)" // "n" matches and "(" does not, no space is needed */ const separatorNeededRegex = /[.\p{XID_Continue}]/u; /** * This function accepts a code string, and returns equivalent code * with comments and unnecessary whitespace removed. */ export function minify(code) { const codeWithoutComments = stripWGSLComments(code); const tokens = codeWithoutComments.split(splitRegex).filter((token) => token !== ''); let result = ''; for (let i = 0; i < tokens.length; i++) { const current = tokens[i]; const next = tokens[i + 1]; invariant(current); result += current; if (isSpaceRequired(current, next)) { result += ' '; } } return result; } function isSpaceRequired(current, next) { if (next === undefined) { return false; } const currentLast = current.at(-1); const nextFirst = next.at(0); invariant(currentLast?.length === 1 && nextFirst?.length === 1, `Expected tokens during minification to not be empty.`); if (separatorNeededRegex.test(currentLast) && separatorNeededRegex.test(nextFirst)) { return true; } if (['//', '/*', '*/'].includes(`${currentLast}${nextFirst}`)) { return true; } return false; } // Based on tint implementation. function stripWGSLComments(code) { let result = ''; let copiedUpTo = 0; let offset = 0; while (offset < code.length) { if (code.startsWith('//', offset)) { result += `${code.slice(copiedUpTo, offset)} `; offset += 2; while (offset < code.length && !lineBreaks.has(code.charAt(offset))) { offset += 1; } copiedUpTo = offset; continue; } if (code.startsWith('/*', offset)) { result += `${code.slice(copiedUpTo, offset)} `; let depth = 1; offset += 2; while (offset < code.length && depth > 0) { if (code.startsWith('/*', offset)) { depth += 1; offset += 2; } else if (code.startsWith('*/', offset)) { depth -= 1; offset += 2; } else { offset += 1; } } if (depth > 0) { throw new SyntaxError(`Block comment opening without corresponding closing found during minification.`); } copiedUpTo = offset; continue; } if (code.startsWith('*/', offset)) { throw new SyntaxError(`Block comment closing without corresponding opening found during minification.`); } offset += 1; } return result + code.slice(copiedUpTo); }