You were making the issue more complex than it needs to be. Below is a rework of your code, with a bit of annotation
Names Default To Here( 1 ); // This line tells JMP to isolate the variable used to this script
// Set a variable to point to the current active data table
dt = Current Data Table();
// Get the column names
colname = dt << get column names( string, continuous );
// Determine the number of columns to loop through
nc = N Items( colname );
// Loop across all of the columns in the colname list
// Your original code was For( i = 1, i < nc, i++,
// with "i < nc" the last colname would not be processed
For( i = 1, i <= nc, i++,
// If this is a column name that starts with "Bin"
// a new % column needs to be added
If( Left( colname[i], 3 ) == "Bin",
// Create the new column
dt << New Column( colname[i] || "%",format("Percent",7,2),
formula( __col__ / :name( "Total" ) ) )
Expr( __col__ ), Parse( ":" || colname[i] )
Lines 4 and 5 are the JSL that is manipulated as an expression; the __col__ is a placeholder.
Lines 3 and 6 are quoting the expression so it doesn't try to run.
Line 7 identifies the bit to be replaced, also quoted so it doesn't execute.
Line 8 is the replacement value. This is interesting because it actually builds some JSL as a concatenation of two strings, then parses it into an expression. The result of the parse function is an expression.
Lines 2 and 9 are the Substitute(source,pattern,replacement) function. It returns an expression built from the three expression arguments.
In case there are special characters in the column name you could use Parse( "As Column \!"" || colname[i] || "\!"" ) in the substitution. The original post tried to use Name(), which is hard to use in scripts as it only accepts a quoted string.