cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
The Discovery Summit 2025 Call for Content is open! Submit an abstract today to present at our premier analytics conference.
Choose Language Hide Translation Bar
View Original Published Thread

Jasean
Staff
Writing JSL code dynamically

Scripters are often confused when they attempt to use a variable as an argument in a JSL function or message but don't get the expected result. While there are many circumstances in JSL where scripters encounter this situation, but the one that users frequently encounter is when they try to set the Spec Limits column property. The values to use as the Spec Limits are often extracted from a lookup table in a previous step where they are assigned to variables. After collecting and assigning all the limit values to variables, the next logical step is to use these variables to set the column property in the analysis table. Here is a representative example of what a scripter might try. You can copy this JSL into a scripting window and run it to follow along.

Names Default to Here(1);

// Open a sample data table
dt = Open("$SAMPLE_DATA/Cleansing.jmp");

// Values previously obtained from a lookup table and assigned to variables as:
myLSL = 6;
myUSL = 8;
myTarget = 7;

// Use the variables to set the spec limits column property
Column(dt, "pH") << Set Property(
    "Spec Limits",
    {LSL( myLSL ), USL( myUSL ), Target( myTarget ), Show Limits( 1 )}
);

When this script is executed, the icon appears next to the pH column indicating a column property is set, and clicking on the icon confirms it is the Spec Limits property.

Jasean_0-1733505426601.png

 

However, when you open the Column Info dialog window to see the values for the Spec Limits, they are missing!

Jasean_1-1733505426607.png

 

What did JMP do? It seemed to work, but only partially! Why?!?

To investigate, we can use JSL to see the property settings. Maybe doing this will provide a clue. Evaluating the following expression,

Column(dt, "pH") << Get Property("Spec Limits");

results in the following information written to the log:

{LSL( myLSL ), USL( myUSL ), Target( myTarget ), Show Limits( 1 )}

Well, there it is, exactly what I specified, but why don't I see the values that were assigned to the variables instead of the variables themselves? The answer to this question gets to the crux of the confusion: JSL does not always evaluate arguments enough times to resolve the assigned value, and the number of times arguments are evaluated varies among circumstances where variables are used. For example, in the present context JMP "parses" the list argument to understand that there are items to specify different aspects of the Spec Limits property. That is one evaluation. In another evaluation, JMP reviews the items of the list to determine which limits (LSL, USL, Target, etc.) to set in the Spec Limits property. JMP does not evaluate the arguments provided inside the list items at all (myLSL, myUSL, myTarget). JMP is expecting numeric values as arguments for LSL, USL, and Target. When a variable is provided, JMP does not recognize it and does not attempt to resolve the variable to determine the associated value.

So, can I know how many times an argument will evaluate in a given circumstance? What can I do if I need more evaluations, as in the example above? Since we don't have access to JMP's source code, it is very difficult to know how many evaluations will be performed in a given circumstance. However, that is less important than knowing if it is enough for the present use. The first thing I do is try and verify! 

Without prior knowledge of how the Spec Limits message expects values, I write my script exactly as initially shown above. Next, I check if the Spec Limit was set by reviewing it in the Column Info dialog or creating a graph with the column to see if the Graph Reference Lines appear as they should. If the verification failed, as in this case, I need to edit my expression to remove the variables and hard code the numeric values instead. 

Column( dt, "pH" ) << Set Property(
	"Spec Limits",
	{LSL( 6 ), USL( 8 ), Target( 7 ), Show Limits( 1 )}
)

I then run the expression to verify the issue is with my use of variables and not some other syntax problem. Having confirmed the expression provides the expected result with the hard-coded values, I know I have encountered a circumstance where my variable is not being evaluated enough to get to the assigned value. Therefore, I need to have JMP create the expression dynamically at run time to embed the variable values into it before evaluating the expression.

Now there are multiple ways to pre-evaluate a variable, but my preferred method is to use the Eval Expr() function. Other methods include using the Substitute() function or building the JSL expression by concatenating strings, then parsing and evaluating the final string. By far my least favorite method is building the expression as a string. The code is very difficult to read because it is monochromatic, so I lose the helpful semantic formatting provided by the JSL editor and embedded quotation marks need to be escaped. Even worse, the code is difficult to debug and troubleshoot because I lose the ability to evaluate selected pieces of the code. 

// An example of concatenating strings to dynamically write a JSL expression.
Parse(
	"Column( dt, \!"pH\!" ) << Set Property(
		\!"Spec Limits\!",
		{LSL( " || Char(myLSL) || " ), USL( " || Char(myUSL) || " ), Target( " || Char(myTarget) || " ), Show Limits( 1 )}
	)"
)

Using the Substitute() function is better in this regard, but if the expression is long, I find it difficult to read the expression while also looking at the latter arguments that specify what and where substitutions are being done.

// An example of using Substitute() to dynamically write a JSL expression.
Substitute(	
	Expr(Column(dt, "pH") << Set Property(
		"Spec Limits",
		{LSL( lll ), USL( uuu ), Target( ttt), Show Limits( 1 )}
	))
,
	Expr(lll), myLSL,
	Expr(uuu), myUSL,
	Expr(ttt), myTarget
)

By contrast, the Eval Expr() function allows for in-line substitution specification and, since the argument to the function is an expression, the JSL editor is able to display it with the semantic formatting.

So how does Eval Expr() work? It is a function that takes an expression as its only argument and returns an expression. It searches the provided expression for the token Expr(). JMP then evaluates what is inside the parentheses of Expr() and replaces the entire token with the return value. Returning to the example above to set the spec limits, if I “wrap” my variables with Expr() and place the entire expression within an Eval Expr() call, JMP will substitute my variables (and the Expr() token) with the variable values at run time and return the modified expression.  So, 

// An example of using Eval Expr() to dynamically write a JSL expression.
Eval Expr(
    Column(dt, "pH") << Set Property(
        "Spec Limits",
        {LSL( Expr(myLSL) ), USL( Expr(myUSL) ), Target( Expr(myTarget) ), Show Limits( 1 )}    );
);

returns:

Column( dt, "pH" ) << Set Property(
    "Spec Limits",
    {LSL( 6 ), USL( 8 ), Target( 7 ), Show Limits( 1 )}
)

That’s great! I now have an expression to set the Spec Limits property that includes the desired hard-coded values. I just wrote JSL code dynamically! Now I just need to evaluate this modified expression. For this, the Eval() function is useful. I can wrap the Eval Expr(), call in an Eval() call, and the expression returned by Eval Expr() will be immediately evaluated.  Here is a complete example script that will set the Spec Limits property correctly using limit values known only at run time:

Names Default to Here(1);

// Open a sample data table
dt = Open("$SAMPLE_DATA/Cleansing.jmp");

// Values previously obtained from a lookup table and assigned to variables as:
myLSL = 6;
myUSL = 8;
myTarget = 7;

// Use the variables to set the spec limits column property
Eval(Eval Expr(
    Column(dt, "pH") << Set Property(
        "Spec Limits",
        {LSL( Expr(myLSL) ), USL( Expr(myUSL) ), Target( Expr(myTarget) ), Show Limits( 1 )}
    );
));

Now when I verify this script, I see values for the Spec Limits in the Column Info dialog.

Jasean_2-1733505426609.png

 

Furthermore, evaluating this expression

Column(dt, "pH") << Get Property("Spec Limits");

results in the following information written to the log:

{LSL( 6 ), USL( 8 ), Target( 7 ), Show Limits( 1 )}

Finally, if I create a graph with the column, I see the Spec Limit reference lines in the expected locations.

Jasean_3-1733505426610.png

 

Writing JSL code dynamically is a useful skill for a scripter to develop. There are many circumstances where JMP does not evaluate an argument in an expression enough times to get to the actual value needed. While there are multiple techniques for writing JSL code dynamically, my preferred method is what I call the Eval(EvalExpr(Expr())) method. It allows for in-line substitution and semantic formatting, which make reading and debugging code relatively easy.

Comments