IF

From ICE Enterprises
Jump to navigation Jump to search

implements a logical block if-then-else function in a macro

<VALUE> Left side of expression
<TEST>  Logical test to be performed.
<VALUE> Right side of expression

There are two forms of the IF statement; both can only be used in a macro.
The first form is the block IF, the second is the one-line IF.

Block IF Statement:
  The block IF statement evaluates a logical expression and, if the logical
  expression is true, executes the set of statements following. If the logical
  expression is false, control transfers to the next ELSE, ELSEIF, or ENDIF
  statement at the same IF level. The syntax is as follows (optional items are
  in braces):

    IF <conditional> THEN
      <action>
    [ELSEIF <conditional> THEN
      <action>
    ]
    [ELSE
      <action>
    ]
    ENDIF

  IF-ELSEIF-ELSEIF-...-ELSE-ENDIF blocks can be nested without limit. Note that
  "THEN" is technically optional, but highly recommended.

One-Line IF Statement:
  The one-line IF requires that the <action> be a single executable Midas
  command, such as GOTO, BREAK, ENDMODE, CALC, etc.  It should not be the start
  or end of a structure (such as WHILE,ENDWHILE,LOOP,ENDLOOP,IF,ENDIF,etc), or
  an operating system command.  For those, the block IF should be used.

    IF <conditional> THEN <action>

  Prior releases indicated that the THEN was optional for the one-line IF;
  this is not always the case. Since NeXtMidas 2.1.2 the THEN is required for
  one-line IF statements.

  WARNING: When using a one-line IF statement the <action> may be cleaned (i.e.
           converted to uppercase) before execution. If this is a problem, use
           the multi-line IF syntax.

Conditional Expressions:
  A <conditional> expression has the following form:
    <value1> <test1> <value2> [ <conjunction> <value3> <test2> <value4> ... ]

  where <conjunction> is either AND or OR.

Tests:
   A EQ B         Numeric A is   equal   to B
   A == B         Numeric A is   equal   to B (since 2.5.0)
   A NE B         Numeric A is not equal to B
   A != B         Numeric A is not equal to B (since 2.5.0)
   A GT B         Numeric A is greater than B
   A >  B         Numeric A is greater than B (since 2.5.0)
   A LT B         Numeric A is   less  than B
   A <  B         Numeric A is   less  than B (since 2.5.0)
   A GE B         Numeric A is greater or equal to B
   A >= B         Numeric A is greater or equal to B (since 2.5.0)
   A LE B         Numeric A is   less  or equal to B
   A <= B         Numeric A is   less  or equal to B (since 2.5.0)

   A EQT B        Alias for EQ/T, see below for details.
   A ~= B         Alias for EQ/T, see below for details. (since 2.5.0)
   A EQTOL B      Old alias for EQ/T (rarely used any more).

                  ***********************************************************
                  * Note: Prior to NeXtMidas 3.3.2 the above tests assumed  *
                  * scalar input values and only considered the first part  *
                  * of any non-scalar input. This behavior was deprecated   *
                  * in NeXtMidas 2.7.0 and removed in NeXtMidas 3.3.2. Now  *
                  * all entries in a non-scalar value are considered.       *
                  ***********************************************************

                  **************************************************************
                  * The following test modifiers for EQ can be combined (e.g.  *
                  * "A EQ/M/T B" compare magnitude of A and B with a tolerance.*
                  **************************************************************

   A EQ/S B       Makes sure both have the same number of elements (size). This
                   is almost always combined with /M or /V.
   A EQ/B B       Use a binary data comparison. Use with caution since this
                    will return false if even the slightest thing is different
                    (e.g. "1 EQ/B 1.0" -- which compares a L: to a D: will fail)
   A EQ/T B       Numeric A is equal to B within tolerance.
                    By default this will use a relative tolerance check using
                    the system-wide tolerance (usually 1e-6). This can be
                    overridden using the following switches:
                      /TOL=<tol>   - Use *relative* tolerance <tol>
                      /DELTA=<tol> - Use *absolute* tolerance <tol> (since 3.3)
                    See nxm.sys.libm.Tolerance for more info on "relative".
   A EQ/M B       Compare the magnitude of the values of each atom
                    (refer to nxm.sys.lib.Data.getMagnitude()).
   A EQ/V B       Compare each value in an atom one-by-one. This is the same as
                    computing vector equality in Euclidean space.
   A NE/? B       Same as NEQ/? where ? is any valid modifier(s) for EQ.

   A GT/M B       Numeric A has greater magnitude than B
   A LT/M B       Numeric A has less    magnitude than B
   A GE/M B       Numeric A has greater or equal magnitude than B
   A LE/M B       Numeric A has less    or equal magnitude than B

   A ANYBITS B    Integer A contains any of the bits set in integer B
   A ALLBITS B    Integer A contains all of the bits set in integer B

                  ***********************************************************
                  * Note: String tests ignore case unless /CS is present!   *
                  ***********************************************************

   A ENDS B       A ends with B.  TRUE if A ends with all the characters in B.
                    This is same as A.endsWith(B).      (Since NeXtMidas 3.1.2)
   A EQS  B       String A equal to string B in length and content
   A EQSS B       EQual Shortest String: Equality is checked only up to the
                    length of the shorter of A or B
   A SEQS B       String A equal to String B (alias for EQS)
   A STARTS B     A starts with B.  TRUE if A starts with all the chars in B.
                    This is same as A.startsWith(B).    (Since NeXtMidas 3.5.0)
   A SUBS B       A is a substring of B.  TRUE if all the characters of A are
                    found in their entirety and in order somewhere within B.

   A FEXISTS      The file named A exists (note that the file existence tests
                    can be customized in the case of a URL, see javadocs for
                    HttpResource for more info).
   A DEXISTS      The directory named A exists
   A REXISTS      The results parameter named A exists
   A PEXISTS      The parameter or switch named A or /A exists for this macro

   A ISNULL       The object A is NULL or does not exist
   A ISTRUE       The state of A resolves to true. [This is the DEFAULT test.]
                  (TRUE states include: Y|YES|T|TRUE|ON|1|PASS|SUCCESS|ABSC|AB)
   A ISFALSE      The state of A resolves to false
                   (FALSE states include: N|NO|F|FALSE|OFF|0|FAIL|INDEX|IN)

   A FEQ   B      File A is equal to File B (same as FEQ/H/D/S, files must be
                    equal in size)
   A FEQ/B B      File A is equal to File B - BINARY comparison
   A FEQ/D B      File A DATA is equal to File B DATA
   A FEQ/H B      File A HEADER is equal to File B HEADER
   A FEQ/K B      File A *extended* header KEYWORDS are equal to File B extended
                    header KEYWORDS.
   A FEQ/L B      File A is equal to File B (limits length of binary or data
                    comparison to size of shorter file, when applicable)
                                                         (Since NeXtMidas 3.7.0)
   A FEQ/S B      File A is equal in size to File B
   A FEQ/T B      File A is equal within *relative* tolerance to File B.
                    See notes about /TOL=<tol> with IF/T (above).

   A OEQ  B       Object A is equal to Object B

   A TYPEOF     B The result A matches the type specified in B (see below).
   A INSTANCEOF B The result A is an instance of class B (see below).
   A CONTAINS   B This is the same as CONTAINS/K test.
   A CONTAINS/K B The table A contains the key B (String). -or-
                  Since 3.5.4, the Map A contains the key B (any Object).
                  FALSE if A is not a java.util.Map. Table is a Map.
                  Since 3.5.4, supports /CS switch to do a case-sensitive key B
                  containment check on the Table.
                  NOTE: Key B containment on Map that is NOT a table, always
                  does an Object check (and hence it is case-sensitive).
   A CONTAINS/V B The Map/Table A contains the value B (via Object.equals).
                  FALSE if A is not a java.util.Map.     (Since NeXtMidas 3.5.4)

  Putting an "N" on the beginning of any test (except for the "C-style" numeric
  tests) will test for its logical opposite, for example:

   A NEQS B       String A does not equal B
   A NEQ  B       Numeric A is not equal to B (same as NE)
   A NFEQ B       The files differ
   A N<   B       Not allowed

  If the <test> is omitted, the ISTRUE state test is assumed.

  Note that ANDs and ORs are short-circuiting; as soon as a condition is
  evaluated which can stop evaluation, evaluation will stop.

TYPEOF vs INSTANCEOF:
  INSTANCEOF matches the Java instanceof operator. The right-hand-argument is
  always the (case sensitive) name of a class or interface. The test will
  return true for "a INSTANCEOF b" if...
    (1) The class of a is the same as b
    (2) The class of a is a subclass of b
    (3) The class of a is a implements of b where b is an interface

  Due to this "a INSTANCEOF java.lang.Object" will always return true since
  all objects are subclasses of java.lang.Object.

  TYPEOF has two sets of functionality. The first takes in the name of a
  Java class as the right-hand-argument and checks to see if the class
  is an *exact* match. In this form, "a TYPEOF b" maps to the Java code
  a.getClass().getName().equalsIgnoreCase(b). The second form takes in the
  name of a Midas format type (e.g. 'D'=double,'F'=float,...) or digraph (e.g.
  "SF"=ScalarFloat,"VD"=VectorDouble,"3D"=ThreeDouble). This test will return
  true for "a TYPEOF b": if...
    (1) The class of a is the same as b (ignoring the case of b)
    (2) The class of a is String and b is "S" (special case)
    (3) The class of a is Data and the type of a matches b where b is a
        single-letter type specifier
    (4) The class of a is Data and the format digraph of a matches b where
        b is a two-letter format digraph (note that this does an "exactness"
        check where "VD" is *not* the same as "3D")

  Due to this "a TYPEOF b" will always return false when b is the name of an
  interface or abstract class since it is doing an exactness check.

Precedence Rules:
  The precedence rules is LEFT to RIGHT between conjunctions (AND|OR).
  Additionally, the logical test will short-circuit when an OR conjunction is
  used and the result is TRUE on the LEFT side, hence all tests to the right
  of the OR conjunction will be ignored and not processed.
  The test will also short-circuit when an AND conjunction is used and the
  result is FALSE on the LEFT side, hence all tests to the right of the AND
  conjunction will be ignored and not processed.

  IF 1 LT 2  OR   2 GT 3   AND 3 EQ 4  ! TRUE  (all tests after OR  are skipped)
  IF 1 EQ 2 AND "A" EQ "B"  OR 3 EQ 3  ! FALSE (all tests after AND are skipped)

  Current versions of NeXtMidas permit parenthesis around the test cases which
  give more control over the precedence.

    IF ( (1 LT 2) OR (2 GT 3)  AND (3 EQ 4))  ! TRUE
    IF (((1 LT 2) OR (2 GT 3)) AND (3 EQ 4))  ! FALSE

String Rules:
  The following rules apply to the string comparisons (SUBS,EQSS,EQS,etc.):
    - String tests are Case Insensitive unless the /CS switch is used.
    - Length of a string literal is the number of characters between the quotes.
    - Trailing spaces are not trimmed.
    - If an argument to a string test is a switch name and that switch is not
      present the string will contain the text "NULL" and can be tested
      appropriately.

    WARNING: The results of EQS, EQSS and SUBS comparisons are different from
             X-Midas, since X-Midas treats "" and " " as being equal, NeXtMidas
             does not do this.

Examples:
  Numeric Tests:
    Given that D1=D:1.0, D2=D:1.00001, L1=L:1
      IF D1 EQ   D2           ! FALSE (Different numbers)
      IF D1 EQ   L1           ! TRUE  (Same numbers)
      IF D1 EQ/B L1           ! FALSE (Different bits for D: vs L:)
      IF D1 EQT  D2           ! FALSE (use default  tolerance, normally 1e-6)
      IF D1 EQT  D2 /TOL=1e-5 ! TRUE  (use relative tolerance of 1e-5)

      IF 100 EQ/T 99.95     /TOL=1e-6        ! FALSE (relative tolerance)
      IF 100 EQ/T 99.999999 /TOL=1e-6        ! TRUE  (relative tolerance)
      IF 100 EQ/T 99.95     /DELTA=0.2       ! TRUE  (absolute tolerance)
      IF 100 EQ/T 99.999999 /DELTA=0.0000002 ! FALSE (absolute tolerance)

  String Tests:
    Given that LS is the MAX of LA and LB.  A EQS B is TRUE if LA equals LB and
    all characters of both strings are the same.
      RESULTS A:S1 "A "      ! S1 has length 2
      RESULTS A:S2 " "       ! S2 has length 1

      IF "A"   EQS  "A "     ! FALSE (Different lengths)
      IF "aBc" EQS  "ABC"    ! TRUE  (defaults to non-case sensitive comparison)
      IF "AA"  EQS  "aa" /CS ! FALSE (case sensitive comparison)
      IF " "   EQS  ""       ! FALSE (empty string and blank are different!)
      IF ""    EQS  "A"      ! FALSE ("" does not equal "A")
      IF S1    EQS  "A"      ! FALSE
      IF S1    EQS  "A "     ! TRUE
      IF S2    EQS  ""       ! FALSE (empty string and blank are different!)
      IF S2    EQS  " "      ! TRUE
      IF S2    EQS  "A"      ! FALSE

      IF "A"   EQSS "A "     ! TRUE  (the first chars are the same)
      IF "A "  EQSS "A"      ! TRUE  (same as above, order is not important)
      IF S1    EQSS "A   "   ! TRUE  (same as above)
      IF " "   EQSS ""       ! TRUE  (empty string is EQSS with any string)
      IF "ABC" EQSS ""       ! TRUE  (same as above)
      IF S2    EQSS "ABC"    ! FALSE (S2 starts with a ' ')
      IF S1    EQSS S2       ! FALSE (S2 starts with a ' ')
      IF "A "  EQSS " A"     ! FALSE (leading space is important)
      IF "A"   EQSS " A"     ! FALSE (leading space is important)
      IF "AB"  EQSS "BAB"    ! FALSE (first 2 characters not the same)
      IF "A "  EQSS "AB "    ! FALSE (first 2 characters not the same)
      IF "A"   EQSS "AB"     ! TRUE  (first character is the same)
      IF " AB" EQSS " "      ! TRUE

      IF "ABC" STARTS "A"    ! TRUE (First string starts with "A")
      IF "Abc" STARTS "b"    ! FALSE
      IF "Abc" STARTS "a" /CS! FALSE (Fails case sensitive starts with check.)

      IF "A"   SUBS "A "     ! TRUE  ("A" found in the first character)
      IF "A"   SUBS "BCDA"   ! TRUE  ("A" found at the 4th character)
      IF S1    SUBS "A"      ! FALSE
      IF "^S1" SUBS "A"      ! FALSE
      IF "A "  SUBS "A"      ! FALSE
      IF "A "  SUBS "CA B"   ! TRUE  ("A " found at the second character)
      IF " "   SUBS ""       ! FALSE (no space at all on right hand side)
      IF S1    SUBS S2       ! FALSE (no leading string "A" in an empty string)
      IF S2    SUBS S1       ! TRUE
      IF "BCD" SUBS "ABCDE"  ! TRUE

      Invoking methods on a java.lang.String results to do comparisons.
      IF "".isEmpty()        ! ERROR (not supported at this time)
      IF S1.isEmpty()        ! FALSE
      IF S1.contains("A")    ! TRUE  (same as "A" SUBS S1)
      IF S1.contains("a")    ! FALSE (since 3.5.0 lower case passed correctly)
      IF S1.contains(s2)     ! TRUE  (same as S2 SUBS S1)
      IF S1.matches("A.")    ! TRUE  (using regex - 'A' followed by any char)
      IF S1.matches("A*")    ! FALSE (using regex - zero or more 'A's)
      IF S1.matches("A.*")   ! TRUE  (using regex)
      IF S1.startsWith(" ",1) ! TRUE (prefix,toffset)
      IF S1.startsWith("A",1) ! FALSE
      IF S1.regionMatches(1,"ABC ",3,1) ! TRUE  (toffset,other,ooffset,len)
      IF S1.regionMatches(1,"ABC ",3,2) ! FALSE (len does not match)
      IF S1.regionMatches(1,S2,0,1)     ! TRUE
      IF S1.regionMatches(0,"TMA ",2,2) ! TRUE
      IF S1.regionMatches(0,"Da ",1,1)  ! FALSE (since 3.5.0 lower case 'a')
      IF S1.regionMatches(true,0,"Da",1,1) ! TRUE (ignore case)

  File/Directory Tests:
    Gracefully deletes a Midas file.  Note that this is a one-line IF.

        IF file FEXISTS THEN ERASE file

    Recalculates the maximum of the file "myfile", if either REDO has been set
    to 1, or the result "filemax" does NOT exist and the file DOES exist:

        IF redo EQ 1 OR filemax NREXIST AND myfile FEXIST THEN
          MAXMIN myfile filemax
        ENDIF

Switches:
  /CS          - Perform a CASE SENSITIVE test. For EQS, EQSS, ENDS, SEQS,
                 STARTS, SUBS, and CONTAINS (in Table) only. [DEF=IgnoreCase]

  /DELTA=<tol> - The *absolute* tolerance for the EQ/T, and FEQ/T tests. This
                 will override any use of /TOL= without warning (this permits
                 /TOL= to be given at top of macro to override system-wide
                 relative tolerance without restricting use of /DELTA=). If
                 /DELTA= is not specified *relative* tolerance checks will be
                 used. [DEF=<use relative tol>]          (since NeXtMidas 3.3.2)

  /TOL=<tol>   - The *relative* tolerance for the EQ/T, and FEQ/T tests. If not
                 specified (and /DELTA= is not specified) the system-wide
                 *relative* tolerance (usually 1e-6) will be used.

SEE ALSO:  ELSE, ELSEIF, ENDIF, nxm.sys.libm.Tolerance, nxm.sys.lib.Args,
           nxm.sys.lib.Data