Skip to content

Commit 49bf94b

Browse files
committed
Fix detection of local variables in for loop
1 parent daee4a4 commit 49bf94b

File tree

2 files changed

+60
-5
lines changed

2 files changed

+60
-5
lines changed

src/strands/strands_transpiler.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -622,23 +622,41 @@ const ASTCallbacks = {
622622

623623
// Analyze which outer scope variables are assigned in the loop body
624624
const assignedVars = new Set();
625-
const analyzeBlock = (body) => {
625+
const analyzeBlock = (body, parentLocalVars = new Set()) => {
626626
if (body.type !== 'BlockStatement') return;
627627

628+
// First pass: collect variable declarations within this block
629+
const localVars = new Set([...parentLocalVars]);
630+
for (const stmt of body.body) {
631+
if (stmt.type === 'VariableDeclaration') {
632+
for (const decl of stmt.declarations) {
633+
if (decl.id.type === 'Identifier') {
634+
localVars.add(decl.id.name);
635+
}
636+
}
637+
}
638+
}
639+
640+
// Second pass: find assignments to non-local variables
628641
for (const stmt of body.body) {
629642
if (stmt.type === 'ExpressionStatement' &&
630643
stmt.expression.type === 'AssignmentExpression') {
631644
const left = stmt.expression.left;
632645
if (left.type === 'Identifier') {
633-
assignedVars.add(left.name);
646+
// Direct variable assignment: x = value
647+
if (!localVars.has(left.name)) {
648+
assignedVars.add(left.name);
649+
}
634650
} else if (left.type === 'MemberExpression' &&
635651
left.object.type === 'Identifier') {
636652
// Property assignment: obj.prop = value (includes swizzles)
637-
assignedVars.add(left.object.name);
653+
if (!localVars.has(left.object.name)) {
654+
assignedVars.add(left.object.name);
655+
}
638656
}
639657
} else if (stmt.type === 'BlockStatement') {
640-
// Recursively analyze nested block statements
641-
analyzeBlock(stmt);
658+
// Recursively analyze nested block statements, passing down local vars
659+
analyzeBlock(stmt, localVars);
642660
}
643661
}
644662
};

test/unit/webgl/p5.Shader.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,43 @@ suite('p5.Shader', function() {
893893
assert.approximately(pixelColor[2], 77, 5);
894894
});
895895

896+
test('handle nested for loops with state modification between loops', () => {
897+
myp5.createCanvas(50, 50, myp5.WEBGL);
898+
899+
const testShader = myp5.baseMaterialShader().modify(() => {
900+
myp5.getPixelInputs(inputs => {
901+
let total = myp5.float(0.0);
902+
903+
// Outer for loop
904+
for (let i = 0; i < 2; i++) {
905+
let innerSum = myp5.float(0.0);
906+
907+
// Inner for loop
908+
for (let j = 0; j < 3; j++) {
909+
innerSum = innerSum + 0.1; // 3 * 0.1 = 0.3 per outer iteration
910+
}
911+
912+
// State modification between inner and outer loop
913+
innerSum = innerSum * 0.5; // Multiply by 0.5: 0.3 * 0.5 = 0.15
914+
total = total + innerSum; // Add to total: 2 * 0.15 = 0.3
915+
}
916+
917+
inputs.color = [total, total, total, 1.0];
918+
return inputs;
919+
});
920+
}, { myp5 });
921+
922+
myp5.noStroke();
923+
myp5.shader(testShader);
924+
myp5.plane(myp5.width, myp5.height);
925+
926+
// Should be: 2 iterations * (3 * 0.1 * 0.5) = 2 * 0.15 = 0.3
927+
const pixelColor = myp5.get(25, 25);
928+
assert.approximately(pixelColor[0], 77, 5); // 0.3 * 255 ≈ 77
929+
assert.approximately(pixelColor[1], 77, 5);
930+
assert.approximately(pixelColor[2], 77, 5);
931+
});
932+
896933
test('handle for loop using loop variable in calculations', () => {
897934
myp5.createCanvas(50, 50, myp5.WEBGL);
898935

0 commit comments

Comments
 (0)