Skip to content

Commit

Permalink
Changing rename functions to also rename the scope index, and not mutate
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewRayCode committed Jul 7, 2024
1 parent 2397a8d commit 01b3e49
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 70 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"engines": {
"node": ">=16"
},
"version": "4.1.1",
"version": "5.0.0-beta.1",
"type": "module",
"description": "A GLSL ES 1.0 and 3.0 parser and preprocessor that can preserve whitespace and comments",
"scripts": {
Expand Down
52 changes: 43 additions & 9 deletions src/parser/scope.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,10 @@ float fn() {
`);

expect(ast.scopes[0].functions.noise);
renameFunctions(ast.scopes[0], (name) => `${name}_FUNCTION`);
ast.scopes[0].functions = renameFunctions(
ast.scopes[0].functions,
(name) => `${name}_FUNCTION`
);
expect(generate(ast)).toBe(`
float noise_FUNCTION() {}
float fn_FUNCTION() {
Expand Down Expand Up @@ -233,8 +236,14 @@ vec4 linearToOutputTexel( vec4 value ) { return LinearToLinear( value ); }
{ quiet: true }
);

renameBindings(ast.scopes[0], (name) => `${name}_VARIABLE`);
renameFunctions(ast.scopes[0], (name) => `${name}_FUNCTION`);
ast.scopes[0].bindings = renameBindings(
ast.scopes[0].bindings,
(name) => `${name}_VARIABLE`
);
ast.scopes[0].functions = renameFunctions(
ast.scopes[0].functions,
(name) => `${name}_FUNCTION`
);

expect(generate(ast)).toBe(`
float selfref_VARIABLE, b_VARIABLE = 1.0, c_VARIABLE = selfref_VARIABLE;
Expand Down Expand Up @@ -306,7 +315,8 @@ StructName main(in StructName x, StructName[3] y) {
float a2 = 1.0 + StructName(1.0).color.x;
}
`);
renameTypes(ast.scopes[0], (name) => `${name}_x`);
ast.scopes[0].types = renameTypes(ast.scopes[0].types, (name) => `${name}_x`);

expect(generate(ast)).toBe(`
struct StructName_x {
vec3 color;
Expand Down Expand Up @@ -342,22 +352,43 @@ StructName_x main(in StructName_x x, StructName_x[3] y) {
]);
expect(Object.keys(ast.scopes[0].bindings)).toEqual(['reflectedLight']);
expect(Object.keys(ast.scopes[0].types)).toEqual([
'StructName',
'OtherStruct',
'StructName_x',
'OtherStruct_x',
]);
expect(ast.scopes[0].types.StructName.references).toHaveLength(16);
expect(ast.scopes[0].types.StructName_x.references).toHaveLength(16);

// Inner struct definition should be found in inner fn scope
expect(Object.keys(ast.scopes[2].types)).toEqual(['StructName']);
});

test('shangus', () => {
const ast = c.parseSrc(`
struct MyStruct { float y; };
attribute vec3 position;
vec3 func() {}`);

ast.scopes[0].bindings = renameBindings(
ast.scopes[0].bindings,
(name) => `${name}_x`
);
ast.scopes[0].functions = renameFunctions(
ast.scopes[0].functions,
(name) => `${name}_y`
);
ast.scopes[0].types = renameTypes(ast.scopes[0].types, (name) => `${name}_z`);

expect(Object.keys(ast.scopes[0].bindings)).toEqual(['position_x']);
expect(Object.keys(ast.scopes[0].functions)).toEqual(['func_y']);
expect(Object.keys(ast.scopes[0].types)).toEqual(['MyStruct_z']);
});

test('fn args shadowing global scope identified as separate bindings', () => {
const ast = c.parseSrc(`
attribute vec3 position;
vec3 func(vec3 position) {
return position;
}`);
renameBindings(ast.scopes[0], (name) =>
ast.scopes[0].bindings = renameBindings(ast.scopes[0].bindings, (name) =>
name === 'position' ? 'renamed' : name
);
// The func arg "position" shadows the global binding, it should be untouched
Expand All @@ -378,7 +409,10 @@ uniform vec2 vProp;
};`);

// This shouldn't crash - see the comment block in renameBindings()
renameBindings(ast.scopes[0], (name) => `${name}_x`);
ast.scopes[0].bindings = renameBindings(
ast.scopes[0].bindings,
(name) => `${name}_x`
);
expect(generate(ast)).toBe(`
layout(std140,column_major) uniform;
float a_x;
Expand Down
134 changes: 74 additions & 60 deletions src/parser/utils.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import type { AstNode } from '../ast/index.js';
import { Scope } from './scope.js';
import {
FunctionScopeIndex,
Scope,
ScopeIndex,
TypeScopeIndex,
} from './scope.js';

export const renameBindings = (
scope: Scope,
mangle: (name: string, node: AstNode) => string
) => {
Object.entries(scope.bindings).forEach(([name, binding]) => {
bindings: ScopeIndex,
mangle: (name: string) => string
) =>
Object.entries(bindings).reduce<ScopeIndex>((acc, [name, binding]) => {
const mangled = mangle(name);
binding.references.forEach((node) => {
if (node.type === 'declaration') {
node.identifier.identifier = mangle(node.identifier.identifier, node);
node.identifier.identifier = mangled;
} else if (node.type === 'identifier') {
node.identifier = mangle(node.identifier, node);
node.identifier = mangled;
} else if (node.type === 'parameter_declaration' && node.identifier) {
node.identifier.identifier = mangle(node.identifier.identifier, node);
node.identifier.identifier = mangled;
/* Ignore case of:
layout(std140,column_major) uniform;
uniform Material
Expand All @@ -25,72 +31,80 @@ export const renameBindings = (
throw new Error(`Binding for type ${node.type} not recognized`);
}
});
});
};
return {
...acc,
[mangled]: binding,
};
}, {});

export const renameTypes = (
scope: Scope,
mangle: (name: string, node: AstNode) => string
) => {
Object.entries(scope.types).forEach(([name, type]) => {
types: TypeScopeIndex,
mangle: (name: string) => string
) =>
Object.entries(types).reduce<TypeScopeIndex>((acc, [name, type]) => {
const mangled = mangle(name);
type.references.forEach((node) => {
if (node.type === 'type_name') {
node.identifier = mangle(node.identifier, node);
node.identifier = mangled;
} else {
console.warn('Unknown type node', node);
throw new Error(`Type ${node.type} not recognized`);
}
});
});
};
return {
...acc,
[mangled]: type,
};
}, {});

export const renameFunctions = (
scope: Scope,
mangle: (name: string, node: AstNode) => string
) => {
Object.entries(scope.functions).forEach(([fnName, overloads]) => {
Object.entries(overloads).forEach(([signature, overload]) => {
overload.references.forEach((node) => {
if (node.type === 'function') {
node['prototype'].header.name.identifier = mangle(
node['prototype'].header.name.identifier,
node
);
} else if (
node.type === 'function_call' &&
node.identifier.type === 'postfix'
) {
// @ts-ignore
const specifier = node.identifier.expression.identifier.specifier;
if (specifier) {
specifier.identifier = mangle(specifier.identifier, node);
functions: FunctionScopeIndex,
mangle: (name: string) => string
) =>
Object.entries(functions).reduce<FunctionScopeIndex>(
(acc, [fnName, overloads]) => {
const mangled = mangle(fnName);
Object.entries(overloads).forEach(([signature, overload]) => {
overload.references.forEach((node) => {
if (node.type === 'function') {
node['prototype'].header.name.identifier = mangled;
} else if (
node.type === 'function_call' &&
node.identifier.type === 'postfix'
) {
// @ts-ignore
const specifier = node.identifier.expression.identifier.specifier;
if (specifier) {
specifier.identifier = mangled;
} else {
console.warn('Unknown function node to rename', node);
throw new Error(
`Function specifier type ${node.type} not recognized`
);
}
} else if (
node.type === 'function_call' &&
'specifier' in node.identifier &&
'identifier' in node.identifier.specifier
) {
node.identifier.specifier.identifier = mangled;
} else if (
node.type === 'function_call' &&
node.identifier.type === 'identifier'
) {
node.identifier.identifier = mangled;
} else {
console.warn('Unknown function node to rename', node);
throw new Error(
`Function specifier type ${node.type} not recognized`
);
throw new Error(`Function for type ${node.type} not recognized`);
}
} else if (
node.type === 'function_call' &&
'specifier' in node.identifier &&
'identifier' in node.identifier.specifier
) {
node.identifier.specifier.identifier = mangle(
node.identifier.specifier.identifier,
node
);
} else if (
node.type === 'function_call' &&
node.identifier.type === 'identifier'
) {
node.identifier.identifier = mangle(node.identifier.identifier, node);
} else {
console.warn('Unknown function node to rename', node);
throw new Error(`Function for type ${node.type} not recognized`);
}
});
});
});
});
};
return {
...acc,
[mangled]: overloads,
};
},
{}
);

export const xor = (a: any, b: any): boolean => (a || b) && !(a && b);

0 comments on commit 01b3e49

Please sign in to comment.