@antonio-domenic , I believe that what you're wanting is to determine, before attempting to create a column based on a formula, whether the table contains the appropriate columns for the given formula? If you're able to determine this before attempting to create the column you'll have less likelihood of running into script-stopping errors.
The good news is that it's pretty simple to determine all of the external names a formula relies on -- which should be only column names of the current data table. (It's best practice to not have a formula rely on a currently in-scope variable that will lose scope at some point in the future, causing the formula to error out).
Here's some example code with a function that recursively parses expressions (formulas are of course just expressions) for any dangling names. You can then check the column names of the table and apply if the column names satisfy the formula:
Names Default to Here( 1 );
dt= New Table( "Test",
Add Rows( 1 ),
New Column( "A",
Numeric,
"Continuous",
Format( "Best", 12 ),
Set Values( [2] )
),
New Column( "B",
Numeric,
"Continuous",
Format( "Best", 12 ),
Set Selected,
Set Values( [1] )
)
);
//recursively find all unassigned names within the formula -- we can test these against the column names
get dangling names = Function( {formula},
{Default Local},
parse formula = Function( {expr, names = {}, assigns = {}},
{Default Local},
If( Contains( Head Name( expr ), "[" ) & Contains( Head Name( expr ), "]" ), // deal with the exceptional case where JMP doesn't arg-parse out this pattern...
items = Words( Head Name( expr ), "[" );
Summation( i = 1, N Items( items ),
Try(
item = Word( 1, items[i], "]" );
If( Is Missing( Num( item ) ),
{n, a} = Recurse( Parse( item ) );
Insert Into( names, n );
Insert Into( assigns, a );
)
);
0
)
,
If( Head Name( expr ) == "Assign", // if a variable is assigned within the formula then we don't care to check it (the formula shouldn't be direclty affecting other columns, it bad practice!)
Insert Into( assigns, Char( Arg( expr, 1 ) ) )
,
Head Name( expr ) != Char( Name Expr( expr ) ), // Need to recursively parse here
Summation( i = 1, N Arg( expr ),
{n, a} = Recurse( Arg( expr, i ) );
Insert Into( names, n );
Insert Into( assigns, a );
0
);
,
Insert Into( names, Char( Name Expr( expr ) ) ) // found a name
);
);
Eval List( {Associative Array( names ) << Get Keys, Associative Array( assigns ) << Get Keys} )
);
{names, assigns} = parse formula( Name Expr( formula ) );
names = Associative Array( names );
assigns = Associative Array( assigns );
names << Remove( assigns );
names = names << Get Keys;
names
);
formulas = {};
Insert Into( formulas, Expr( :Name("C") - :Name("B") ) );
local var = [1 => 3, 2 => 4];
Insert Into( formulas, Eval Expr(
var = Expr( local var );
var[:C][:F] + :D
);
);
Insert Into( formulas, Expr( :A + :B ) );
Insert Into( formulas, Expr( :A + :B - Sin( + Cos( :E - :Name("flamingo@fish" ) ) ) ) );
For( i = 1, i <= N Items( formulas ), i++,
table columns = dt << Get Column Names( "String" );
Write( "\!N" );
Show( table columns );
formula references = get dangling names( formulas[i] );
refs = {};
For( j = 1, j <= N Items( formula references ), j++,
refs[j] = Contains( table columns, formula references[j] ) > 0
);
Show( formulas[i] );
Show( formula references );
Print( If( Sum( refs ) == N Items( formula references ), "OKAY", "NOT OKAY" ) );
If( Sum( refs ) == N Items( formula references ), // The table contains all the necessary columns, okay to add the new column!
Eval( Eval Expr(
dt << New Column( "FORMULA", <<Formula( Expr( formulas[i] ) ) )
) )
)
)
Let me know if you have any questions!
Jordan,
Jordan