Tuesday, November 29, 2011

INTERPRET??  WDNNS INTERPRET!

(For those who don't know what 'wdnns' means, it is a reference to a misquote of a line from Treasure of the Sierra Madre and for the rest, you can look it up on the web.  The expansion is "We don' need no steenking ..."  'Nuf said.)

It is my firmly held conviction that INTERPRET is almost never required for ordinary REXX processing.  Les Koehler has come up with one or two scenaria where it is useful but they are so bizarre that 'esoteric' is a risible understatement.  Most uses of INTERPRET run something like this:

/*  'parm' looks like  ' name=Smith '   */
parse var parm  tag "=" tagval
interpret tag "=" tagval

I hope I got that right.  Since I never use INTERPRET and I don't have a mainframe to test this out, I have to guess as to what the code would actually look like.  If I were writing this code, I would use VALUE instead.

parse var parm tag '=' tagval
rc = Value(tag,tagval)

I have also seen entire commands constructed piece-by-piece and then INTERPRETed to cause the command to be executed.  Unnecessary.

interpret "ISPEXEC TBCREATE" tblnm blah blah blah

I have never seen a case where the INTERPRET couldn't simply be converted to a straight execution:

"ISPEXEC TBCREATE" tblnm blah blah blah

Anyone with a true instance of a necessary INTERPRET is welcome to present the same here.  I'd love to be proven wrong.

11 comments:

  1. I agree the Interpret is way over used but here is one example where I did have to use it. in this code Im displaying an ISPF panel with ISPF Table then reading in the user selection and calling another program with the same name as the selection. I had to build the a string with the varable info and then use INTERPRET to run the command. It was the simplest way i could think of that i could use 2 lines of code and sead it with the varable info.

    do while parmPnlExit
    ADDRESS ISPEXEC
    "TBTOP PARMSTBL"
    "TBDISPL PARMSTBL PANEL(PCU1PD) CSRROW(0) AUTOSEL(NO)"
    if ZTDSELS > 0 then do
    if S01 = 'S' | S01 = 's' then do
    ejobnm = STRIP(djobnm,BOTH)
    jParmExe = 'call' jparm 'ejobnm' 'pchlq'
    S01 = ''
    inTERPRET JParmExe
    end/*if s01*/
    end /*if ztdsels */

    ReplyDelete
    Replies
    1. Could not that INTERPRET be cast as

      (jparm) ejobnm pchlq

      ??

      I don't have access to a MVS system to test it, and I'd appreciate it if you could try it and report back.

      Delete
  2. I once wrote a routine to translate JCL to REXX for use with PL/I Inspect/Plitest. To prevent various users from overwriting the generated REXX ("Ixxxxx", with xxxxx the name of the program) all code was stored into one "Ixxxxx" member per program, and every user had their own section, preceded by their userid as label.

    Selection was done through a

    interpret 'signal' userid()

    And an edit macro that functions as a command-line calculator:

    "isredit macro(parm)"
    interpret 'result = ('parm')'

    ReplyDelete
  3. I really cannot find any situation where INTERPRET is needed. Don't get me wrong, but any valid reason I used it was quickly replaced with VALUE. I do a lot of data manipulation and dynamic REXX. INTERPRET may save a line or two of code, but a seasoned programmer would trade one or two lines for readability and maintainability.

    ReplyDelete
    Replies
    1. About the only place INTERPRET is required is, as Prino noted above, when it's necessary to branch to a variable location. That's a task that seemingly cannot be handled by any alternative method.

      Delete
    2. Well I am not sure I follow his entire argument ... but I can access any variable with VALUE.

      Example:

      line = "this is my line"
      the_value = VALUE("line")

      Now the_value is "this is my line"

      it works with variables as well:

      line = "this is my line"
      var = "LINE"
      the_value = VALUE(var)

      Now the_value is "this is my line"

      You can also assign a value:

      line = "this is my line"
      the_value = VALUE("line","This is my new line")

      Now the_value is "this is my line"
      But the variable LINE has "This is my new line")

      Once you know this power ... you can stack the VALUEs x =VALUE(VALUE(some_var)) ... that as long as the other variable contains a valid variable name ... it works just dandy. I like to think of this as the "C" construct of the memory locations.

      In my programs, I usually try to keep it simple ... just to make sure it is understandable. Here are some practical snippets of code (forgive me I am doing this by memory).

      /* initializations */
      var_names = "loc_db2 dbname tsname partition msglevel"
      var_default = "DBX1 DBNAM001 TSNAM01 1 ? "

      DO var# = 1 TO WORDS(var_names)
      x = VALUE(WORD(var_names,var#),WORD(var_default,var#))
      END

      So after:
      loc_db2 = "DBX1"
      dbname = "DBNAM001"
      tsnam e = "TSNAM01"
      partition = "1"
      msglevel = "?"

      You may say .. "cute code" but there are other advantages of having your variable names in a single variable ... so that you can have the following as well:

      DO var# = 1 TO WORDS(var_names)
      var_name = WORD(var_names,var#)
      SAY LEFT(var_name,20) "=" VALUE(var_name)
      END

      This is a nice little routine that your program can call if you hit an unexpected call ... you can even include this in a SIGNAL .. but be careful of PROCEDURE and EXPOSE ... but that is another subject

      Delete
    3. Can you [CALL Value(location)] ? No, you can't.

      Can you [signal value(location)] ? No, you can't.

      To do either you must use INTERPRET. That's what Prino is driving at.

      As to 'initialization', I find it cheaper and clearer to
      [ parse value "DBX1 DBNAM001 TSNAM01 1 ? " with,
      loc_db2 dbname tsname partition msglevel . ]

      One statement; no loops. Put it in a callable location and access it whenever needed.

      Delete
    4. Ah ... I see your point.

      As for the using PARSE ... yes I do that as well (much more often than the example I use) for the simplicity and power. I use the other method for very complex programs that need a bit more debugging mode or even better signal handling.

      By having my variables in
      var_names = "loc_db2 dbname tsname partition msglevel"

      Then early in my program I would have something like:

      SIGNAL ON SYNTAX NAME Signal_Handle
      SIGNAL ON NOVALUE NAME Signal_Handle

      Then add

      Signal_Handle:

      SIGNAL OFF SYNTAX
      SIGNAL OFF NOVALUE

      add some displays on CONDITION('C') CONDITION('D)
      some more about variable SIGL and SOURCELINE(SIGL)

      Then I could just loop through the variables

      DO var# = 1 TO WORDS(var_names)
      var_name = WORD(var_names,var#)
      SAY LEFT(var_name,20) "=" VALUE(var_name)
      END

      I sometimes use a msglevel variable to drive if I want to show various variables for debugging purposes that is driven by outside parms without having to turn on and off TRACE.

      Or whatever control I need. This is all overkill for smaller programs ... but really nice for larger programs. I keep various "models" of REXX to start with to make that decision.

      Delete
    5. Building tracing capability into the original is never a waste for any program with at least one CALL.

      As for other capabilities, see http://home.roadrunner.com/~mvsrexx/REXX/ and check out REXXSKEL (which I use as a basis for almost everything I write). Yes, it has more capability than I typically need, but like PREGO spaghetti sauce "it's in there".

      Delete
    6. List comprehension where you apply an arbitrary piece of rexx to every (for example) word in a string in turn. Not VITAL to use interpret but can make it a lot easier.

      Delete
    7. Martin, can you post a short example? I'm not sure what you're driving at.

      Delete