@@ -1142,6 +1142,177 @@ const ASTCallbacks = {
11421142 return replaceInNode ( node ) ;
11431143 }
11441144 }
1145+
1146+ // Helper function to check if a function body contains return statements in control flow
1147+ function functionHasEarlyReturns ( functionNode ) {
1148+ let hasEarlyReturn = false ;
1149+ let inControlFlow = 0 ;
1150+
1151+ const checkForEarlyReturns = {
1152+ IfStatement ( node , state , c ) {
1153+ inControlFlow ++ ;
1154+ if ( node . test ) c ( node . test , state ) ;
1155+ if ( node . consequent ) c ( node . consequent , state ) ;
1156+ if ( node . alternate ) c ( node . alternate , state ) ;
1157+ inControlFlow -- ;
1158+ } ,
1159+ ForStatement ( node , state , c ) {
1160+ inControlFlow ++ ;
1161+ if ( node . init ) c ( node . init , state ) ;
1162+ if ( node . test ) c ( node . test , state ) ;
1163+ if ( node . update ) c ( node . update , state ) ;
1164+ if ( node . body ) c ( node . body , state ) ;
1165+ inControlFlow -- ;
1166+ } ,
1167+ ReturnStatement ( node ) {
1168+ if ( inControlFlow > 0 ) {
1169+ hasEarlyReturn = true ;
1170+ }
1171+ }
1172+ } ;
1173+
1174+ if ( functionNode . body && functionNode . body . type === 'BlockStatement' ) {
1175+ recursive ( functionNode . body , { } , checkForEarlyReturns ) ;
1176+ }
1177+
1178+ return hasEarlyReturn ;
1179+ }
1180+
1181+ // Helper function to check if an if-statement's consequent contains a return
1182+ function blockContainsReturn ( block ) {
1183+ let hasReturn = false ;
1184+ const findReturn = {
1185+ ReturnStatement ( ) {
1186+ hasReturn = true ;
1187+ }
1188+ } ;
1189+ if ( block ) {
1190+ recursive ( block , { } , findReturn ) ;
1191+ }
1192+ return hasReturn ;
1193+ }
1194+
1195+ // Transform a helper function to use __returnValue pattern instead of early returns
1196+ function transformHelperFunction ( functionNode ) {
1197+ // 1. Add __returnValue declaration at the start of function body
1198+ const returnValueDecl = {
1199+ type : 'VariableDeclaration' ,
1200+ declarations : [ {
1201+ type : 'VariableDeclarator' ,
1202+ id : { type : 'Identifier' , name : '__returnValue' } ,
1203+ init : null
1204+ } ] ,
1205+ kind : 'let'
1206+ } ;
1207+
1208+ if ( ! functionNode . body || functionNode . body . type !== 'BlockStatement' ) {
1209+ return ; // Can't transform arrow functions with expression bodies
1210+ }
1211+
1212+ functionNode . body . body . unshift ( returnValueDecl ) ;
1213+
1214+ // 2. Restructure if statements: move siblings after if with return into else block
1215+ function restructureIfStatements ( statements ) {
1216+ for ( let i = 0 ; i < statements . length ; i ++ ) {
1217+ const stmt = statements [ i ] ;
1218+
1219+ if ( stmt . type === 'IfStatement' && blockContainsReturn ( stmt . consequent ) && ! stmt . alternate ) {
1220+ // Find all subsequent statements
1221+ const subsequentStatements = statements . slice ( i + 1 ) ;
1222+
1223+ if ( subsequentStatements . length > 0 ) {
1224+ // Create else block with subsequent statements
1225+ stmt . alternate = {
1226+ type : 'BlockStatement' ,
1227+ body : subsequentStatements
1228+ } ;
1229+
1230+ // Remove the subsequent statements from this level
1231+ statements . splice ( i + 1 ) ;
1232+
1233+ // Recursively process the new else block
1234+ restructureIfStatements ( stmt . alternate . body ) ;
1235+ }
1236+ }
1237+
1238+ // Recursively process nested blocks
1239+ if ( stmt . type === 'IfStatement' ) {
1240+ if ( stmt . consequent && stmt . consequent . type === 'BlockStatement' ) {
1241+ restructureIfStatements ( stmt . consequent . body ) ;
1242+ }
1243+ if ( stmt . alternate && stmt . alternate . type === 'BlockStatement' ) {
1244+ restructureIfStatements ( stmt . alternate . body ) ;
1245+ }
1246+ } else if ( stmt . type === 'ForStatement' && stmt . body && stmt . body . type === 'BlockStatement' ) {
1247+ restructureIfStatements ( stmt . body . body ) ;
1248+ } else if ( stmt . type === 'BlockStatement' ) {
1249+ restructureIfStatements ( stmt . body ) ;
1250+ }
1251+ }
1252+ }
1253+
1254+ restructureIfStatements ( functionNode . body . body ) ;
1255+
1256+ // 3. Transform all return statements to assignments
1257+ const transformReturns = {
1258+ ReturnStatement ( node ) {
1259+ // Convert return statement to assignment
1260+ node . type = 'ExpressionStatement' ;
1261+ node . expression = {
1262+ type : 'AssignmentExpression' ,
1263+ operator : '=' ,
1264+ left : { type : 'Identifier' , name : '__returnValue' } ,
1265+ right : node . argument || { type : 'Identifier' , name : 'undefined' }
1266+ } ;
1267+ delete node . argument ;
1268+ }
1269+ } ;
1270+
1271+ recursive ( functionNode . body , { } , transformReturns ) ;
1272+
1273+ // 4. Add final return statement
1274+ const finalReturn = {
1275+ type : 'ReturnStatement' ,
1276+ argument : { type : 'Identifier' , name : '__returnValue' }
1277+ } ;
1278+
1279+ functionNode . body . body . push ( finalReturn ) ;
1280+ }
1281+
1282+ // Main transformation pass: find and transform helper functions with early returns
1283+ function transformHelperFunctionEarlyReturns ( ast ) {
1284+ const helperFunctionsToTransform = [ ] ;
1285+
1286+ // Collect helper functions that need transformation
1287+ const collectHelperFunctions = {
1288+ VariableDeclarator ( node , ancestors ) {
1289+ const init = node . init ;
1290+ if ( init && ( init . type === 'ArrowFunctionExpression' || init . type === 'FunctionExpression' ) ) {
1291+ if ( functionHasEarlyReturns ( init ) ) {
1292+ helperFunctionsToTransform . push ( init ) ;
1293+ }
1294+ }
1295+ } ,
1296+ FunctionDeclaration ( node , ancestors ) {
1297+ if ( functionHasEarlyReturns ( node ) ) {
1298+ helperFunctionsToTransform . push ( node ) ;
1299+ }
1300+ } ,
1301+ // Don't transform functions that are direct arguments to call expressions
1302+ CallExpression ( node , ancestors ) {
1303+ // Arguments to CallExpressions are base callbacks, not helpers
1304+ // We skip them by not adding them to the transformation list
1305+ }
1306+ } ;
1307+
1308+ ancestor ( ast , collectHelperFunctions ) ;
1309+
1310+ // Transform each collected helper function
1311+ for ( const funcNode of helperFunctionsToTransform ) {
1312+ transformHelperFunction ( funcNode ) ;
1313+ }
1314+ }
1315+
11451316 export function transpileStrandsToJS ( p5 , sourceString , srcLocations , scope ) {
11461317 // Reset counters at the start of each transpilation
11471318 blockVarCounter = 0 ;
@@ -1156,7 +1327,11 @@ const ASTCallbacks = {
11561327 delete nonControlFlowCallbacks . IfStatement ;
11571328 delete nonControlFlowCallbacks . ForStatement ;
11581329 ancestor ( ast , nonControlFlowCallbacks , undefined , { varyings : { } } ) ;
1159- // Second pass: transform if/for statements in post-order using recursive traversal
1330+
1331+ // Second pass: transform helper functions with early returns to use __returnValue pattern
1332+ transformHelperFunctionEarlyReturns ( ast ) ;
1333+
1334+ // Third pass: transform if/for statements in post-order using recursive traversal
11601335 const postOrderControlFlowTransform = {
11611336 IfStatement ( node , state , c ) {
11621337 state . inControlFlow ++ ;
0 commit comments