From c13fb6e4d0e1251257d2779304eec0f85b9e7095 Mon Sep 17 00:00:00 2001 From: Andrew Ray Date: Sun, 28 Jul 2024 13:30:11 -1000 Subject: [PATCH 1/3] Function macro expansion bug --- src/preprocessor/preprocessor.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/preprocessor/preprocessor.test.ts b/src/preprocessor/preprocessor.test.ts index dec17e5..f2524c3 100644 --- a/src/preprocessor/preprocessor.test.ts +++ b/src/preprocessor/preprocessor.test.ts @@ -333,6 +333,19 @@ foo`; foo`); }); +test(`function macro where source variable is same as macro argument`, () => { + const program = ` +#define GE(x, y) x + y +float z = GE(y, x); +`; + + const ast = parse(program); + preprocessAst(ast); + expect(generate(ast)).toBe(` +float z = y + x; +`); +}); + test("macro that isn't macro function call call is expanded", () => { const program = ` #define foo () yes expand From 9ce4b36aad26355267a0e3b0e03495989060ab62 Mon Sep 17 00:00:00 2001 From: Andrew Ray Date: Sun, 28 Jul 2024 15:23:17 -1000 Subject: [PATCH 2/3] Fixing the issue and bumping package version --- package.json | 2 +- src/preprocessor/preprocessor.test.ts | 12 ++++++++-- src/preprocessor/preprocessor.ts | 34 +++++++++++++++++++-------- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index d8febdc..c6fe9cd 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "engines": { "node": ">=16" }, - "version": "5.0.0", + "version": "5.1.0", "type": "module", "description": "A GLSL ES 1.0 and 3.0 parser and preprocessor that can preserve whitespace and comments", "scripts": { diff --git a/src/preprocessor/preprocessor.test.ts b/src/preprocessor/preprocessor.test.ts index f2524c3..0ddc16a 100644 --- a/src/preprocessor/preprocessor.test.ts +++ b/src/preprocessor/preprocessor.test.ts @@ -336,13 +336,21 @@ foo`); test(`function macro where source variable is same as macro argument`, () => { const program = ` #define GE(x, y) x + y -float z = GE(y, x); +GE(y, x); +GE(y.y, x.x); +GE(yy, xx); `; const ast = parse(program); preprocessAst(ast); + + // Ensure that if the argument passed to the fn GE(X) has the + // same name as the macro definition #define GE(X), it doesn't get expanded + // https://github.com/ShaderFrog/glsl-parser/issues/31 expect(generate(ast)).toBe(` -float z = y + x; +y + x; +y.y + x.x; +yy + xx; `); }); diff --git a/src/preprocessor/preprocessor.ts b/src/preprocessor/preprocessor.ts index 3015e58..0d1714d 100644 --- a/src/preprocessor/preprocessor.ts +++ b/src/preprocessor/preprocessor.ts @@ -198,17 +198,31 @@ const expandFunctionMacro = ( throw new Error(`'${macroName}': Not enough arguments for macro`); } + // Collect the macro identifiers and build a replacement map from those to + // the user defined replacements + const argIdentifiers = macroArgs.map( + (a) => (a as PreprocessorIdentifierNode).identifier + ); + const argKeys = argIdentifiers.reduce>( + (acc, identifier, index) => ({ + ...acc, + [identifier]: args[index].trim(), + }), + {} + ); + const replacedBody = tokenPaste( - macroArgs.reduce( - (replaced, macroArg, index) => - replaced.replace( - new RegExp( - `\\b${(macroArg as PreprocessorIdentifierNode).identifier}\\b`, - 'g' - ), - args[index].trim() - ), - macro.body + macro.body.replace( + // Replace all instances of macro arguments in the macro definition + // (the arg separated by word boundaries) with its user defined + // replacement. This one-pass strategy ensures that we won't clobber + // previous replacements when the user supplied args have the same names + // as the macro arguments + new RegExp( + '(' + argIdentifiers.map((a) => `\\b${a}\\b`).join(`|`) + ')', + 'g' + ), + (match) => (match in argKeys ? argKeys[match] : match) ) ); From a0ca97385dea86f037073a7fd131bc7bf6679b34 Mon Sep 17 00:00:00 2001 From: Andrew Ray Date: Sun, 28 Jul 2024 15:36:29 -1000 Subject: [PATCH 3/3] generic name of macro in test --- src/preprocessor/preprocessor.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/preprocessor/preprocessor.test.ts b/src/preprocessor/preprocessor.test.ts index 0ddc16a..8e74dbb 100644 --- a/src/preprocessor/preprocessor.test.ts +++ b/src/preprocessor/preprocessor.test.ts @@ -335,17 +335,17 @@ foo`); test(`function macro where source variable is same as macro argument`, () => { const program = ` -#define GE(x, y) x + y -GE(y, x); -GE(y.y, x.x); -GE(yy, xx); +#define FN(x, y) x + y +FN(y, x); +FN(y.y, x.x); +FN(yy, xx); `; const ast = parse(program); preprocessAst(ast); - // Ensure that if the argument passed to the fn GE(X) has the - // same name as the macro definition #define GE(X), it doesn't get expanded + // Ensure that if the argument passed to the fn FN(X) has the + // same name as the macro definition #define FN(X), it doesn't get expanded // https://github.com/ShaderFrog/glsl-parser/issues/31 expect(generate(ast)).toBe(` y + x;