Statements - Petewg/harbour-core GitHub Wiki
🔙 Home
-
See also: New language statements in xhb-diff.txt
-
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 customFOR 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 useEXIT
statement inside the loop to stop iterationFOR 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
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 anEXIT
clause is not present in between (fall-through). In other words, anEXIT
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:
- the main SWITCH <var> or <expression> must evaluates to an acceptable
numeric
orcharacter
value. - If the SWITCH value is of character type, comparison against CASE values is case sensitive and exact. (==)
- 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
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
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