wgsl-plus
Version:
A WGSL preprocessor, prettifier, minifier, obfuscator, and compiler with C-style macros, conditional compilation, file linking, and multi-format output for WebGPU shaders.
84 lines (83 loc) • 3.6 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.evaluateExpression = evaluateExpression;
/**
* Evaluates a preprocessor conditional expression (whether conditions are true or false).
*/
function evaluateExpression(expr, defines) {
// Replace defined() operator
expr = expr.replace(/defined\s*\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)/g, (_, name) => {
return defines.has(name) ? "1" : "0";
});
// Special case for common pattern: RENDERER == VULKAN where RENDERER="VULKAN" and VULKAN is undefined
// First, collect all macros that expand to identifiers
const macroValueToName = new Map();
for (const [name, macro] of defines.entries()) {
if (!macro.params && macro.value && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(macro.value)) {
macroValueToName.set(macro.value, name);
}
}
// Detect and handle equality comparisons in a C preprocessor compatible way
const equalityRegex = /\b([a-zA-Z_][a-zA-Z0-9_]*)\s*==\s*([a-zA-Z_][a-zA-Z0-9_]*)\b/g;
let match;
while ((match = equalityRegex.exec(expr)) !== null) {
const [fullMatch, left, right] = match;
const leftMacro = defines.get(left);
const rightMacro = defines.get(right);
// Case 1: Left is a defined macro with value equal to right (which is undefined)
if (leftMacro && !leftMacro.params && leftMacro.value === right && !rightMacro) {
expr = expr.replace(fullMatch, "1");
continue;
}
// Case 2: Right is a defined macro with value equal to left (which is undefined)
if (rightMacro && !rightMacro.params && rightMacro.value === left && !leftMacro) {
expr = expr.replace(fullMatch, "1");
continue;
}
// Case 3: Both are defined macros
if (leftMacro && rightMacro && !leftMacro.params && !rightMacro.params) {
// If both macros expand to the same value, it's true
if (leftMacro.value === rightMacro.value) {
expr = expr.replace(fullMatch, "1");
continue;
}
}
}
// Replace undefined identifiers and function-like macros with 0
expr = expr.replace(/\b([a-zA-Z_][a-zA-Z0-9_]*)\b/g, (match, identifier) => {
const macro = defines.get(identifier);
if (macro && !macro.params) {
// For defined macros, use their value
return macro.value || "0";
}
else {
// For undefined identifiers or function-like macros without args
return "0";
}
});
// Process the expression to convert C-style operators to JavaScript
expr = expr.replace(/(\s*)(!=|==|&&|\|\||!)(\s*)/g, (match, before, op, after) => {
switch (op) {
case '!=': return before + '!==' + after;
case '==': return before + '===' + after;
case '&&': return before + '&&' + after;
case '||': return before + '||' + after;
case '!': return before + '!' + after;
default: return match;
}
});
// For standalone numbers (not part of comparisons), treat non-zero as true
if (/^\s*\d+\s*$/.test(expr)) {
const val = parseInt(expr.trim(), 10);
return val !== 0;
}
try {
// Evaluate the expression directly
return eval(expr) ? true : false;
}
catch (e) {
// Log the error for debugging
console.error(`Evaluation error for expression: "${expr}". ${e.message}`);
return false; // Default to false on error
}
}
;