In this section I explain you what I know about Clipper/xBase programming, concentrating on how to produce source code compatible with all the Open Source compilers.
Let's try to highlight the parts of helloworld:
function MAIN * This is an example clear ?"Hello, the weather is fine today" return
(you can obtain this by using the on-line service CodeColorizer at http://www.chami.com/colorizer/).
We will comment each line of the helloworld program.
function MAIN
The first line defines a function named MAIN. Defining such a function is compulsory with (x)Harbour, as if it is missed errors would occur during compilation.
We will learn later how to define and use functions and procedures.
* This is an example
The second line is a comment. Commenting your programs will help you when you are to modify them later. If they aren't commenting, modifying them will be a very hard task. It will be much more difficult if you are to modify programs written by others if they didn't comment them: figuring out what a program does using only its code is very difficult and even the most clean programming language may prove to be write-only.
You can write comments in many different styles: using the asterisk (*), two sweeps (//), a double ampersand (&&) or a couple sweep - asterisk (/*) and asterisk - sweep (*/), as you see below:
* This is a comment... // ...and so is this... && ...and this. /* This is an example of the fourth commenting style, which may span over several lines.*/
The second and the fourth commenting styles are derived from the C programming language, the first and the third are peculiar of the Clipper/xBase standard.
clear
No, the purpose of this command is not to make the program clear, but instead to clean the screen. You could also use clear screen or cls, although these two commands are not exactly the same of the simple clear (the difference will be clear later, when we can GET the point - then, you could also appreciate the joke in this paragraph).
?"Hello, the weather is fine today"
The ? is a command which means print. In the BASIC programming language, it is also an abbreviation for its print command (the syntax of xBase, especially when dealing with flow control, is quite similar to that of the BASIC programming language).
In this case ? print the string Hello, the weather is fine today. Please note that the string
return
Return is used to terminate the function. We will explain later what the return exactly does.
When one creates a program, he will probably write several files and call a function defined in a file from another function in another file. This feature is so easy to apply that it was the first thing I learned how to do with Harbour, just after I learned how to define a function and created my first program, and so I present it just after your first program :-).
ifexample.prg Function MAIN() LOCAL number INPUT "Key in a number: " TO number IF number % 2 = 0 ? "You keyed in an even number" ELSE ? "You keyed in an odd number" ENDIF RETURN ifexample2.prg Function MAIN() LOCAL number INPUT "Key in a number: " TO number IF number % 2 = 0 ? "You keyed in an even number" ? "I can prove it:" ? "the result of ",number," % 2 = 0 is ", number % 2 = 0 ELSE ? "You keyed in an odd number" ? "I can prove it:" ? "the result of ",number," % 2 = 0 is ", number % 2 = 0 ENDIF RETURN local.prg function MAIN LOCAL nNumber := 0 // INPUT "Key in a number: " TO nNumber // IF nNumber < 50 ? "Less than 50" ELSEIF nNumber = 50 ? "Is equal to 50" ELSE ? "Greater than 50" ENDIF menu.prg Function MAIN * Display menu choices. @ 3, 25 PROMPT "First choice" @ 4, 25 PROMPT "Second choice" * Get menu key. MENU TO choice * Perform an action based on your menu choice. DO CASE CASE choice = 0 RETURN CASE choice = 1 DO One CASE choice = 2 DO Two ENDCASE RETURN nihilist.prg Function MAIN() LOCAL number INPUT "Key in a number: " TO number IF number = 0 ? "Congratulations, you keyed the fabolous number " ENDIF ? number RETURN One.prg function One ? "One" return sumup.prg Function MAIN LOCAL num, somma, n CLS ? "Let's sum up the first n odd numbers." INPUT "How many numbers: " TO n somma=0 num=1 DO WHILE num <= 2*n somma=somma+num num=num+2 ENDDO ? "The sum is ",somma Two.prg function Two ? "Two" return
Flow control is the single point in which xBase syntax shows its similarities with BASIC.
In this section I will presume that you know already how to program in another programming language, and I will answer to the same question I had to ask myself when I began working with xBase: How do I call the basic flow-control commands (if, for, while...) ?
The place in which I found the answers has already been cited, http://www.clipx.net/norton.php, yet here I collect all the structures in a single page so that you don't have to surf an hypertext to read what you need. Remember anyway that there you will find more informations. Here you will find instead enjoyable examples.
IF forks a program: it evaluates a condition and executes one of several alternative blocks of statements.
We will now see an example of how the if function works. This example is very easy: inputs a number from the keyboard and uses the if to decide whether the number is even or odd.
Function MAIN() LOCAL number INPUT "Key in a number: " TO number IF number % 2 = 0 ? "You keyed in an even number" ELSE ? "You keyed in an odd number" ENDIF RETURN
By LOCAL number we tell the program that we are about to use a variable named 'number'.
The syntax of the input instruction is also very easy: Key in a number is what the program will show on the screen, TO number is where the program will put the variable read from the keyboard.
The IF instruction is more complex: it is five lines long (it ends surprisingly with the keyword ENDIF). It contains two instructions, ? "You keyed in an even number" and ? "You keyed in an odd number", and executes the first or the second instruction according to the result of the expression number % 2 = 0. In this statement we recall the variable number, that stores the number we entered, and make the computation with the operator %, whose name is modulus and that returns a number representing the remainder of the first number divided by the second.
Then, it makes the comparison with 0 using the = operator. This operator returns the two values accepted by the IF: true or false, whose names in Clipper/xBase are respectively .T. and .F..
If the result is .T. the program executes the first instruction or block of instructions it encounters, if the result is .F. it executes the first instruction or block of instructions after the ELSE keyword. In the following example we show what block of instructions means.
Function MAIN() LOCAL number INPUT "Key in a number: " TO number IF number % 2 = 0 ? "You keyed in an even number" ? "I can prove it:" ? "the result of ",number," % 2 = 0 is ", number % 2 = 0 ELSE ? "You keyed in an odd number" ? "I can prove it:" ? "the result of ",number," % 2 = 0 is ", number % 2 = 0 ENDIF RETURN
Try compiling this program and see what it does. In this example, you see also that to print more strings, or strings and variables with the same ? instruction, you only need to put a comma to separate the various items passed to the command.
We will specify a last thing: the ELSE statement is not compulsory. You can omit it, as the following example does:
Function MAIN() LOCAL number INPUT "Key in a number: " TO number IF number = 0 ? "Congratulations, you keyed the fabolous number " ENDIF ? number RETURN
As you see, this program always prints the number entered by the user, but seems to get happy when the user enters the number 0 (this is an example of nichilist programming).
LOCAL nNumber := 0
//
IF nNumber < 50
? "Less than 50"
ELSEIF nNumber = 50
? "Is equal to 50"
ELSE
? "Greater than 50"
ENDIF
To execute one of several sets of statements depending on which of a set of associated conditions is true.
If none of the preceding CASE conditions are true, the statements following the OTHERWISE statement are executed up to the next ENDCASE statement.
* Display menu choices.
@ 3, 25 PROMPT "First choice"
@ 4, 25 PROMPT "Second choice"
* Get menu key.
MENU TO choice
* Perform an action based on your menu choice.
DO CASE
CASE choice = 0
RETURN
CASE choice = 1
DO One
CASE choice = 2
DO Two
ENDCASE
RETURN
To repeatedly execute a series of statements while a condition is true (.T.).
The next program we will key in is of some interest for the mathematicians (Real Programmers aren't afraid of maths, do you know this famous adage?)
Here is it:
Function MAIN LOCAL num, somma, n CLS ? "Let's sum up the first n odd numbers." INPUT "How many numbers: " TO n somma=0 num=1 DO WHILE num <= 2*n somma=somma+num num=num+2 ENDDO ? "The sum is ",somma
As you can see, this looping statement is similar to the IF statement: both of them are ended by a END-corresponding statement, both of them contains a logical espression.
This looping statement will continue until its condition remains true (will evaluate to .T.).
The two instructions it repeats are somma=somma+num and num=num+2. The second is fundamental: if it wasn't there or was wrong (for example if you keyed in num=num/2), the condition would not evaluate to .F. and the program would not stop its execution (this is called infinite looping). When this happens to you, press the keys Ctrl and C at the same time. This should convince the computer to give his attention to you instead of running the loop.
The WHILE has the control of the expression in its head, as opposed to the REPEAT-UNTIL looping statement which has the control of the condition in its tail (these are Italian expressions, I do not know how English express the same concept). How do the xBase/Clipper say REPEAT-UNTIL? It doesn't. Here is how to emulate it:
The FOR...NEXT construct is most useful for traversing arrays.
alfanum.prg function MAIN a := "12" b := "3" ? a+b return data.prg function MAIN a:=date() && system date ? a+1 b:=ctod("12-12-91") c:=ctod("12-12-06") ? a-b return dbcreate.prg FUNCTION MAIN CREATE clientes USE clientes APPEND BLANK REPLACE FIELD_NAME WITH "CODIGO" REPLACE FIELD_TYPE WITH "C" REPLACE FIELD_LEN WITH 5 APPEND BLANK REPLACE FIELD_NAME WITH "NOMBRE" REPLACE FIELD_TYPE WITH "C" REPLACE FIELD_LEN WITH 30 CLOSE RETURN mismatch.prg function MAIN a := "1" b := 2 ? a + b return num.prg function MAIN a := 1 b := 2 ? a+b
If this was a tutorial about a normal programming language, it would already have been finished. Now it is time to move on its most interesting features.
dbcreate.prg FUNCTION MAIN CREATE names USE names APPEND BLANK REPLACE FIELD_NAME WITH "name" REPLACE FIELD_TYPE WITH "C" REPLACE FIELD_LEN WITH 30 APPEND BLANK REPLACE FIELD_NAME WITH "address" REPLACE FIELD_TYPE WITH "C" REPLACE FIELD_LEN WITH 30 CLOSE CREATE NAMES FROM NAMES RETURN
and this is the next piece of code:
db1.prg function MAIN clear ? "First database example program" select 1 use names append blank replace name with "Mario Rossi" replace address with "Development Road, 1" quit
From the Guide To CA-Clipper 5.3 comes this useful example:
function MAIN // This example is a procedure that simulates an interactive CREATE utility: CreateDatabase("NewFile") RETURN FUNCTION CreateDatabase( cNewDbf ) CREATE TmpExt// Create empty structure extended USE TmpExt lMore := .T. DO WHILE lMore // Input new field definitions APPEND BLANK CLEAR @ 5, 0 SAY "Name.....: " GET Field_name @ 6, 0 SAY "Type.....: " GET Field_type @ 7, 0 SAY "Length...: " GET Field_len @ 8, 0 SAY "Decimals.: " GET Field_dec READ lMore := (!EMPTY(Field_name)) ENDDO // Remove all blank records DELETE ALL FOR EMPTY(Field_name) PACK CLOSE // Create new database file CREATE (cNewDbf) FROM TmpExt ERASE TmpExt.dbf RETURN NIL // This example creates a new definition in a structure extended // file for a character field with a length of 4000 characters: APPEND BLANK REPLACE Field_name WITH "Notes",; Field_type WITH "C",; Field_len WITH 4000 % 256,; Field_dec WITH INT(4000 / 256)