Statements - Petewg/harbour-core GitHub Wiki

🔙 Home

Harbour Statements


  • Harbour supports For Each ... Next enumeration loop with the following syntax:

         FOR EACH <var1> [, <varN>] IN <expr1> [, <exprN>] [DESCEND]
            <code...>
            [IF condition]
               [ EXIT ]
            [ENDIF]
           
            [IF condition]
               [ LOOP ]
            [ENDIF]
            <code...>
         NEXT
  • traversed value <expr1> can be a string, an array, an hash array or an object with custom FOR EACH action

  • enumerator variable <var1> stores a reference to the element of an hash or array specified by <expr1> and thus assignments to the enumerator changes the value of given element of iterated item. (however, this not apply to strings, unless they have passed explicitly by reference. --see bellow)

  • Up to 255 <var> <exp> pairs can be used.

  • the enumerator variable supports the following properties:

      :__enumIndex   - the loop counter for variable
      :__enumKey     - the hash key value of traversed hash item pair
      :__enumBase    - the value that is being traversed
      :__enumValue   - the value of variable
      :__enumIsFirst - is it the first enumerated item?
      :__enumIsLast  - is it the last enumerated item?
    
  • defining new class or overloading existing one, the user can define his own behavior of FOR EACH iterating overloading chosen of above methods and/or the following ones:

       :__enumStart
       :__enumSkip
       :__enumStop
    
  • after the loop the controlling variable(s) restore the value which they had before entering the loop

       a := 'A'
       b := 'B'
       FOR EACH a, b IN { 1, 2, 3, 4 }, "abcd"
          ? a, b   // prints: 1 a
                   //         2 b
                   //         3 c
                   //         4 d
       NEXT
       ? a, b      // prints: A B (values they had before 'For Each .. Next' loop)
  • array and hashes elements are always passed by reference while elements of a string are being passed by value (although, passing a string by reference is still possible -see next note).

         string := "abc"
         arr := { 1, 2, 3 }
         FOR EACH c, e IN string, arr
            c := Upper( c )
            e *= 2
         NEXT
         // now  'arr' stores { 2, 4, 6 }
         // but  'string' still stores "abc" and not "ABC"
  • when the FOR ... EACH loop is used to iterate string items and the string is passed by reference any assignment to enumerated items affects (changes) the original string, i.e.:

          cString := "abcd2qwer"
          FOR EACH c IN @cString
             IF ! isAlpha( c )
                c := "*"
             ELSE
                c := upper( c )
             ENDIF
          NEXT
          ? cString  // --> ABCD*QWER
  • By default the FOR ... EACH iterates (that is, goes through) all members of the given object but you can use EXIT statement inside the loop to stop iteration

         FOR EACH a IN { 1, 2, 3, 4 }
            IF a:__enumindex == 3
               ? a
               EXIT
            ENDIF
         NEXT
  • Special note: (notice the difference when iterator is enclosed in parenthesis)

         FOR EACH a IN someValue
            ? a:__enumindex   // prints current value of the index
            ? (a):__enumindex // sends '__enumindex' message to the current value
         NEXT

🔙 Home


SWITCH ... ENDSWITCH

Harbour supports SWITCH ... ENDSWITCH statement with the following syntax:

    SWITCH <Var>|<expression>

       CASE <val>
          ...
          EXIT
       CASE <val>
          ...
          EXIT 

       [ OTHERWISE ] 
          ...

    END[SWITCH]

It is somehow similar to DO CASE ... ENDCASE with two major differences.

  • CASE values must be either numeric or string constants (other value types are rejected by compiler)
  • All the code below the first successful CASE is executed up to the ENDSWITCH, if an EXIT clause is not present in between (fall-through). In other words, an EXIT clause is mandatory to avoid executing code encapsulated into no-matching CASEs, unless such an effect is intentional by design.

Due the fact that in a SWITCH statement Harbour uses a jump table with predefined values, SWITCH is significantly faster than a DO CASE statement where sequential PCODE evaluation is used. If no matching CASE value is found, then code covered by OTHERWISE clause (if any) is always executed.

As CASE values Harbour supports numeric (integer) and string constants, f.e.:

   SWITCH x
      case 1         
         ...
         EXIT
      case 10002
         ...
         EXIT
      case "data"
         ...
         EXIT
      otherwise
         ...
   ENDSWITCH

NOTES:

  1. the main SWITCH <var> or <expression> must evaluates to an acceptable numeric or character value.
  2. If the SWITCH value is of character type, comparison against CASE values is case sensitive and exact. (==)
  3. Fall-through behavior. If one CASE matches and no EXIT clause is present inside that code, all the following CASEs below and up to the next EXIT or ENDSWITCH, will be regarded as successful, which means their code will be processed! for example:
x := 1
SWITCH x
   CASE 0
      ...
      EXIT

   CASE 1   // here we have a matching, so..
      ...   // this code is executed, but since there is no EXIT clause..

   CASE 2   // "Fall-through" effect
      ...   // ..this code is also executed, although it doesn't actually match!
      EXIT  // and it's here where the execution flow is redirected outside
            // the SWITCH structure, towards code found after ENDSWITCH
   CASE 3
      ...
ENDSWITCH

🔙 Home


BEGIN SEQUENCE ... END [SEQUENCE]

The syntax is:

      LOCAL oErr // variable needed to be defined whenever Errorblock is used.
      BEGIN SEQUENCE [ WITH ErrorBlock( {| oErr | Break( oErr ) } ) ]

           <code>

      [ RECOVER [ USING oErr ] ]

           <recoverCode>

      [ ALWAYS ]

           <alwaysCode>

      END [ SEQUENCE ]      

Is guaranteed that, as the word "ALWAYS" explicitly denotes, the code found inside the <alwaysCode> segment will be executed «always!», (including any case of abnormal event such as runtime errors). The lines into <alwaysCode> will be processed even if inside the <code> or <recoverCode> code-segments appear some 'escape' statements or commands like: RETURN, BREAK or QUIT.
Please note that when the following code is executed:

   BEGIN SEQUENCE
      <code>
   ALWAYS
      <alwaysCode>
   END SEQUENCE

without RECOVER clause, then BREAK exception inside <code> is not recovered, <alwaysCode> is executed and then BREAK exception is passed to outer BEGIN SEQUENCE (i.e., program execution jumps to the code line(s), if any, found under the END SEQUENCE statement). This is the same semantic as used in TRY / [ CATCH ] / FINALLY / END.

Those who prefer to use TRY / CATCH / FINALLY / END instead of BEGIN SEQUENCE / RECOVER / ALWAYS / END can simply add to their source code:

   #command TRY               => BEGIN SEQUENCE
   #command CATCH [ oErr ]    => RECOVER [ USING <oErr> ]   

and:

   errorBlock( {|oErr| break( oErr ) } )    

Though instead of break( oErr ) it is suggested to use some small function which will support some basic recovery/substitute operations like in default errorsys() and LockErrHandler() to not break existing code which may depends on the default behavior. Note also, that in Harbour ALWAYS code is execute even for QUIT exception. When <alwaysCode> is executed current exception is stored and restored when is finished. If new exception appears inside <alwaysCode> then restored exception depends on the priority in the following order:

   QUIT     // highest priority
   BREAK
   RETURN   

If both exceptions have the same priority and contain additional value (error object in BREAK or return value in RETURN) then the recently set one is restored. It's similar behavior to destructor code.

🔙 Home


WITH OBJECT ... ENDWITH

Harbour supports the following statement:

       WITH OBJECT <object>
         ...
       ENDWITH
  • Inside this WITH OBJECT ... END enclosure you can use the simplified form of sending messages to the object.
    You can use the syntax:
         :message( [params] )
         :property

to send messages to the object specified by <object>, for example:

    WITH OBJECT myobj:a[ 1 ]:myitem
       :message( 1 )
       :value := 9
    END[WITH]

The above code is equivalent to:

    myobj:a[ 1 ]:myitem:message( 1 )
    myobj:a[ 1 ]:myitem:value := 9
  • Inside WITH OBJECT/END you can access (or even assign a new object) using a special reserved property:
         :__withobject
  • Note: A runtime error will be generated at the time of message sending (or property access/assign) if is not a value of type object.

Example usage:

     CREATE CLASS foo
        VAR name INIT 'FOO'
     ENDCLASS

     CREATE CLASS bar
        VAR name INIT 'BAR'
     ENDCLASS

     WITH OBJECT foo():new()
        ? :name                 // prints 'FOO'
        ? :__withobject:name    // also prints 'FOO'
        ? :__withobject := bar():new()
        ? :name                 // prints 'BAR'
     END[WITH]

🔙 Home

⚠️ **GitHub.com Fallback** ⚠️