Reply
This is an open group. Sign in and click the "Join Group" button to become a group member and start posting.
Highlighted
Community Member

Eval (), Parse (), and Recursion, an Adventure in Meta-Programming

A few years ago, I was working on building a script to automagically generate a sampling plan for an attribute-style measurement systems analysis. Everything was going smoothly, until I ran into a big problem: how could I make the script pull up a window to generate the master “Pass/Fail” list of parts being used, if the number of parts was chosen at run-time? Now, you might be already thinking of an easy way to do this. I’m still, however, proud of the method that I came up with, and I’ll share that in this blog post.

Hidden in the Programming portion of the JSL Scripting Index are 2 seemingly innocuous entries: Eval and Parse. These functions allow a crafty scripter to execute a string as if it were a piece of code. Being able to do this has staggering implications. Doing this can also solve my problem from earlier.

Let’s dig in by building a simple script:

//section 1:

//get pass/fail criteria for one part

//initialize some variables

i=1;

listParts = {};

//build modal window to get criteria

New Window("Get Pass/Fail List",

       <<Modal,

              H List Box(

                     V List Box(

                           Lineup Box( 2,

                                  Text Box(char(i)), listParts[i] = Text Edit Box("Pass"),

                                  Button Box("OK", listParts[i] = listParts[i] << Get Text;),       Button Box("Cancel")

                           )                   

                     )

              )

       );

//look at list

show(listParts[i]);

Make sure you have the log showing, then run this script. The script itself shows a window and asks, for one part only, if the part is a “Pass” or a “Fail.” When the OK button is pressed, whatever text is in the window is passed to the ith location in the listParts list, and listParts is shown on the log. Storing the results in a list of just 1 item seems kind of silly for now, but makes sense when we have more than one part to store Pass/Fail criteria for. See the example below, for what the script looks like for 3 parts:

//section 2:

//repeat of above script, but grabbing criteria for 3 parts

i=1;

listParts = {};

New Window("Get Pass/Fail List",

       <<Modal,

              H List Box(

                     V List Box(

                           Lineup Box( 2,

                                  Text Box(char(1)), listParts[1] = Text Edit Box("Pass"),

                                  Text Box(char(2)), listParts[2] = Text Edit Box("Pass"),

                                  Text Box(char(3)), listParts[3] = Text Edit Box("Pass"),

                                  Button Box("OK", for (i = 1, i <=3, i++, listParts[i] = listParts[i] << Get Text;)),     Button Box("Cancel")

                           )                   

                     )

              )

       );

show(listParts);

But, we haven’t solved the problem, yet. How can we make this script show a dynamic number of text boxes, based on some arbitrary number of parts, that isn’t known until after the script starts running, and could be different each time the script is run? Enter Eval and Parse, the aforementioned wunderkinds, to the rescue. Take a look at the following code:

//section 3:

//determine number of parts

New Window ("How many parts?",

       << Modal,

       neb = Number Edit Box (10),

       Button Box ("OK", numParts = neb << Get;)

);

show(numParts);

listParts = {};

//Build first portion of script for modal window

//Escape character sequence for " is \!"

a = "

       New Window(\!"Get Pass/Fail List\!",

            <<Modal,

              H List Box(

                     V List Box(

                           Lineup Box( 2,

";

//for loop creates second portion, which builds however many input boxes as there are parts

// || is the shortcut for concatenate

b="";

for ( i = 1, i <=numParts, i++,

       b1 = char("Text Box(char(" || char(i) || ")), listParts[" || char(i) || "] = Text Edit Box(\!"Pass\!"),");

       b = b||b1;

);

//build last portion of script for modal window

c = "

       Button Box(\!"OK\!", for (i = 1, i <=numParts, i++, listParts = listParts << Get Text;)),     Button Box(\!"Cancel\!")

                           )                   

                     )

              )

       )

";

//combine parts of script, and execute

d = a || b || c;

eval (parse(d));

//look at what was created

show(d);

show(listParts);

First, there’s a section to ask the user how many parts, which is stored in numParts. Next, the script creates three separate strings that look suspiciously like JSL, except they are all in purple. The first string is just an exact copy (with some special code to make the quote characters not end the string) of the script from before, all the way up to the part where text boxes are inserted. The second part is a for loop to build a string consisting of however many parts were entered previously (numParts) repeats of textboxes. The third section is the last bits of the script from before.

Once this string is created, we combine them (d = a || b || c;), then use the eval (parse(d)); command to evaluate what happens when JMP parses the string d (our dynamically created string with an adjustable number of text boxes) as if the string d were JSL script, and not just a string. Look at the log to see both the super long command to show lots of text boxes, as well as the list of Pass/Fail that was generated (shown below if you just accept the defaults):

d = "

       New Window(\!"Get Pass/Fail List\!",

       <<Modal,

              H List Box(

                     V List Box(

                           Lineup Box( 2,

Text Box(char(1)), listParts[1] = Text Edit Box(\!"Pass\!"),Text Box(char(2)), listParts[2] = Text Edit Box(\!"Pass\!"),Text Box(char(3)), listParts[3] = Text Edit Box(\!"Pass\!"),Text Box(char(4)), listParts[4] = Text Edit Box(\!"Pass\!"),Text Box(char(5)), listParts[5] = Text Edit Box(\!"Pass\!"),Text Box(char(6)), listParts[6] = Text Edit Box(\!"Pass\!"),Text Box(char(7)), listParts[7] = Text Edit Box(\!"Pass\!"),Text Box(char(8)), listParts[8] = Text Edit Box(\!"Pass\!"),Text Box(char(9)), listParts[9] = Text Edit Box(\!"Pass\!"),Text Box(char(10)), listParts[10] = Text Edit Box(\!"Pass\!"),

       Button Box(\!"OK\!", for (i = 1, i <=numParts, i++, listParts = listParts << Get Text;)),     Button Box(\!"Cancel\!")

                           )                   

                     )

              )

       )

";

listParts = {"Pass", "Pass", "Pass", "Pass", "Pass", "Pass", "Pass", "Pass", "Pass", "Pass"};

Take a moment to think of the implications, here. With these concepts, you can create code that adjusts itself, and then executes itself. Dynamic, self-referential, recursive, and crazy scripts could be just the beginning. Given enough time and patience, you could probably use this concept to create Skynet. On second thought, maybe that’s not such a good idea…