# Clipper Tutorial: a Guide to Open Source Clipper(s)

Jump to navigation Jump to search

Quite a lot of people were interested in this page (it was the most visited page in my former website) and so I feel I have complete it. This page disappeared when GeoCities was closed (in fact it still contains some broken links to GeoCities pages), and it has been reloaded thank to some requests I got, for example on Facebook. There you can find the "Harbour MiniGUI" group, and others.

When (and if) finished, it will prove a complete guide to Open Source Clipper programming. However, this page is still very incomplete. Recently (it is now 16/12/2014) I noticed a new Wikibook on a subject similar to this: http://en.wikibooks.org/wiki/Category:Application_Development_with_Harbour

As a newbie to Wikibooks, I did not yet properly format the few sources in this page (Wikibooks has syntax highlighting for Clipper, but it is a bit buggy). If you check it, you will see misplaced links and so... this page definitely needs proofreading!

I'd like to see this tutorial grow! If someone on the newsgroup comp.lang.clipper or on the mailing lists HarbourUsers (http://lists.harbour-project.org/mailman/listinfo/harbourusers) would give me help or clues. To apologize for its incompleteness, I can only say that this page contains everything I know about Clipper and xBase programming (in a given moment... I always try to learn new things...). I let digressions sneak in here and there.

I tried to adhere to the classical tutorials' bottom-up approach of showing all the basic function via very simple examples. The plan to include some bigger examples, with the top-down approach (how do I deal with this problem?) is suggested, but not yet pursued...

At the moment this guide deals mostly with (x)Harbour under Windows, although I plan to describe other environments.

I have decided to name this tutorial "a Guide to Open Source Clipper(s)" and not "a Guide to Open Source xBase" because I like the name Clipper, and as you can see by watching at the open source compilers I discuss (Clip and (x)Harbour) the influence of the name Clipper is great: only X2c doesn't recall the name Clipper (but now there's the new X#). I now consider the name inadequate.

Modern xBase open source dialects are:

1. Harbour (https://harbour.github.io/)
2. xHarbour (http://www.xharbour.org/)
which are the most active and mature projects.
3. Clip, which apparently exists in two versions on SourceForge: https://sourceforge.net/projects/x-clip/ (v 1.2.1.6, Last Update: 2017-06-04) and https://sourceforge.net/projects/clip-itk/
4. X#, which is an implementation for .NET (https://www.xsharp.info/, https://github.com/X-Sharp/XSharpPublic). It looks interesting but the runtime is missing. The documentation is at https://www.xsharp.info/help/index.html.
5. DBFree (http://www.dbfree.org) is used to create web applications and is free, even if it contains components, such as MaxScript (http://maxscript.org/, http://www.maxsis.it) which are not.
X2c (http://web.archive.org/web/20090416070816/http://x2c.dtop.com/, http://freshmeat.sourceforge.net/projects/x2c) is very old - what use can it have for a modern programmer if the only freely available C compiler is in the Embarcadero Antique Software pages, namely https://cc.embarcadero.com/item/25636, and the download links don't work? However, I like some of its examples and I'll mention them.

This Guide was born when I followed a small project (the port of an old Summer 87 application to Windows) and I saw the many Clipper compatible open source compilers available, but noticed also that there were no good tutorials and that the books about Clipper/xBase/Visual Objects and so on couldn't be found in any bookstore (not even in libraries!). The first objective of these notes was to replicate what happened in a "PC GUIDE", the first of eight booklets of a self-instruction course in the Clipper language bought on an Italian newsstand in 1993.

Some old versions of these notes can be found on GeoCities mirrors - (I'd like to thank W. Birula for letting me know, not to mention his suggestion of uploading my set of notes on Wikibook, and also for the flowchart he kindly provided). Thanks to bpd2000 for the nice link he provided.

The current plan is that the first part of the tutorial will cover the basic of the language, up to the procedural programming facilities and the native DBF file support. Part 2 will explain OOP and other programming topics, and the last two parts will be about programming a user interface and web applications. This makes up for a nice introduction to computer science.

Michele Povigna

1. Getting Acquainted
2. Getting Started
3. Part 1: The Common Ground
1. Chapter 1: Basic Language Tutorial
2. Chapter 2: Working with Databases
4. Part 2: Additions to the Language
1. Chapter 3: Object Oriented Programming
2. Chapter 4: Some More Advanced Programming Topics
5. Part 3: Making Up a User Interface
6. Part 4: Web Applications

## Clipper Tutorial

Please note: if anybody will add his work to this stuff, the Clipper tutorial should contain only 'standard' code that would compile on Harbour, xHarbour and Clip. Compiler-specific code should be placed somewhere else, or in a box.

In practice, (x)Harbour permits a great versatility, as it (they) can be used in four different ways:

1. by running hbrun or xbscript and executing instructions interactively (much like a BASIC direct mode or immediate mode, although it is more similar to the dBase dot prompt). The main limit of this approach is that it cannot run expressions longer than a single line (but it is possible to enter more instructions on a line separating them with semicolons). However, much of my tutorial is thought to be entered, tested and understood one line at a time.
2. by calling hbrun or xbscript specifying a .prg file as a line argument to execute it (which is again like specifying a file to run when invoking a BASIC interpreter)
3. by compiling the file to a bytecode using the /gh option of the Harbour compiler and then running the resulting .hrb bytecode file with hbrun (this is similar to the workflow in Java, when you call the compiler javac and then the Java interpreter on the bytecode file)
4. by using the Harbour compiler, C compiler and linker to get an executable file (utilities are given to get all the steps done in a single command)
5. by using the compiler through an IDE

There are also commercial RADs, like Xailer (https://www.xailer.com/) or xHarbour Builder (https://www.xharbour.com/).

Antonino Perricone wrote an extension for Visual Studio Code, which is well documented at: https://github.com/APerricone/harbourCodeExtension/wiki, https://medium.com/harbour-magazine/visual-studio-code-for-harbour-e148f9c1861a, https://harbour.wiki/index.asp?page=PublicArticles&mode=show&id=190401174818&sig=6893630672. The main problem with the precompiled version of Visual Studio Code is that it's not under the MIT license.

Packages providing syntax highlighting for various editors are available: for Sublime Text (https://www.sublimetext.com/) is available at https://github.com/asistex/Sublime-Text-harbour-Package, for SynWrite (http://www.uvviewsoft.com/synwrite/) at https://github.com/rafathefull/synwrite, for Atom Editor (https://atom.io/) at https://github.com/AtomLinter/linter-harbour, UltraEdit at http://forums.ultraedit.com/syntax-highlighting-wordfile-for-harbour-fivewin-t17880.html. I myself like Scintilla (https://www.scintilla.org/), a lightweight open source editor that supports the xBase syntax highlighting under the name Flagship - which is an implementation different from the open source ones we are considering but that does not make much difference and it's anyway highly configurable.

### Getting Started With hbIDE

1. To create a new project, select File > New > New Project Wizard (Prototype Only). We will name this project hbidetest, enter the path C:/hb32/projects/hbidetest.hbp and click Save and Close.
2. Select File > Open Project in the menu bar.
3. Right click the project hbidetest in the projects dock on the right and "Set as Current" project.
4. Select File > New > Source and create a file hbidetest.prg with the following content:
 function MAIN
* This is an example
clear
?"Hello, the weather is fine today"
?"(this is a test of HbIDE)"
wait && will show "Press any key to continue..." and prevent the console window to close immediately
return

5. Double click hbidetest in the projects dock so that the and click the Select Sources button near the Project Output text box, then select Save and close. It is a button not very well indicated - I think a button clearly stating "Add source file" would have been better.
6. Now after selecting Build > Build and Launch our program will show up.

### The helloworld application line by line

Let's try to highlight the parts of helloworld:

 function MAIN
* This is an example
clear
?"Hello, the weather is fine today"
return nil


(in GeoCities this and all other sources were highlighted by the on-line service CodeColorizer at http://www.chami.com/colorizer/, or, alternatively by the service at http://tohtml.com/Clipper/ - they tend to look a bit better than the Wikibook highlighting).

We will comment each line of the helloworld program.

function MAIN


The first line defines a function named MAIN. Defining such a function is not compulsory with Harbour, as if it is missed errors would occur during compilation, but I will keep it to make sure the examples work with all compilers. A function cannot be defined when typing in commands at the dot prompt (hbrun/xbscript).

Every xBase language is case insensitive.

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 if not properly commented.

You can write comments in many different styles: using an asterisk (*), two sweeps (//), a double ampersand (&&) or a couple sweep-asterisk (/*) and asterisk-sweep (*/), as shown 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. None of these comments are accepted by xbscript, but hbrun (Harbour) accepts the last three.

clear


No, the purpose of this command is not to make the source code clear (that would be too easy! It is up to us to write clear source code, and to do so we must comment it well), but instead to clean the screen. :-) You could also use clear screen or cls, although these two commands are not exactly the same of this clear (the difference will be clear later, when we can GET the point - then, you could also appreciate the pun in this paragraph - but will more likely not).

What are commands?
Harbour commands are defined in the file std.ch via the #command macro directive (http://harbour.edu.pl/clipper/en/ng122d3c.html). The first commands we encountered are defined by these lines:

#command CLS                        => Scroll() ; SetPos(0,0)
#command ?  [<explist,...>]         => QOut( <explist> )

? "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 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 nil


Return is used to terminate the function. We will explain later what the return exactly does.

Today Harbour needs not an explicitly defined main function/procedure. The following version of helloworld will compile and is exactly the same of the previous one:

&& hello world without commands and main
Scroll() ; SetPos(0,0)
QOut( "Hello world" )


The Harbour Interpreter and Virtual Machine
The program above, stored in a text file named hello.prg, can be run with the command

hbrun hello.prg


hbrun is an interpreter for Harbour. This means that if we run it, we get to a screen with a "Dot prompt" on its last line, where we can enter and run instructions. For example entering

? "Hello world"


will get Hello world printed on the screen, but only after the interpreter hbrun itself is closed. To quit the interpreter and get the greeting printed type

QUIT


All in all, hbrun permits to feel how handling databases was done in the old dBase "Dot prompt" mode - that is, like this: http://manmrk.net/tutorials/database/dbase/IntrodBASEIIIPlus.htm

Running the compiler harbour (harbour.exe on Windows) on a printA.prg containing the line

? "A"


will output a printA.c file. The bottom of this file reads:

HB_FUNC( PRINTA )
{
static const HB_BYTE pcode[] =
{
36,1,0,176,1,0,106,2,65,0,20,1,7
};

hb_vmExecute( pcode, symbols );
}


that is, as an intermediate step the Harbour compiler compiles pcode, or bytecode for a virtual machine.

If the compilation is done with the /gh option

hbmk2 /gh printA.prg
harbour /gh printA.prg


the result will be a file named printA.hrb, a "Harbour Portable Object" file which can be executed by

hbrun printA.hrb


In this way, hbmk2 and hbrun work in couple as a perfectly equivalent tools to the Java compiler javac and java interpreter java.

As a parenthesis, virtual machines are not a new concept. If we look into the retrocomputing field, we'll see that in the late 1970s and early 1980s the University of California San Diego has a portable operating system based on pcode, the UCSD P-System which was written in UCSD Pascal and could be run on 6502, 8080, Z-80, and on PDP-11 http://www.threedee.com/jcm/psystem/index.html (it actually was a competitor of MS-DOS, PC DOS and CP/M - http://www.digitalresearch.biz/CPM.HTM and http://www.seasip.info/Cpm/index.html).

Also, in 1979 Joel Berez and Marc Blank developed the Z-machine as a virtual machine to run Infocom's text adventure games, the name not hiding they were mainly thinking about Zork. The programming language for the Z-machine was called ZIL (Zork Implementation Language). A modern compiler for ZIL to Microsoft .NET is ZILF https://bitbucket.org/jmcgrew/zilf/wiki/Home and a Z-machine.NET https://zmachine.codeplex.com/. More informations at http://inform-fiction.org/zmachine/index.html.

Apart from the JVM (Java Virtual Machine), the Microsoft .NET CLR (Common Language Runtime), is another modern virtual machine.

hbmk2 was created to support all shells, all compilers on all platforms, also to replace old 'bld.bat' solution, while staying compatible with existing hbmk script features and options.

Informations on how to get an executable without using hbmk2 can be found at http://www.kresin.ru/en/hrbfaq.html.

Remember that the PATH Environment Variable (in Windows) must be updated to include the directory containing the compiler: use the command SET PATH=C:\hb32\bin;%PATH% locally in a Prompt Window or update it globally: "Start" → "Control Panel" → "System" → "Advanced system settings" → "Environment Variables..." → select "Path", then click "Edit..."

### Data Types in General (and Their Operators)

The list of types supported by Clipper is as follows:

• A Array
• B (Code) Block
• C Character
• D Date
• L Logical
• M Memo
• N Numeric
• O Object
• U NIL

These letters can be used as a prefix to a variable name, to indicate "at first sight" what kind of item it is. This way oBox would be the name of an object, aNames an array, dBirthday a date and so on. An alternative for logical variables is to add an is prefix as in isOk or isExisting (somewhat more readable than lOk and lExisting). This naming convention is called the Hungarian notation and applies to functions as well, and to their synopsis: ACOPY( <aSource>, <aTarget>, [<nStart>], [<nCount>], [<nTargetPos>] ) is the function to copy elements from an array to another, which takes two arrays aSource and aTarget, and optionally three numeric parameters nStart, nCount, nTargetPos; CToD(cDate) takes a character argument and converts it to a date, DBCreate() is a function to create a database.

The type Memo can only be used in a database (it is a link to a subsidiary file to a DBF table, as described in the Wikipedia DBF entry).

This is the list of results returned by the function VALTYPE(). The function TYPE() returns also:

• U NIL, local, or static
• UE Error syntactical
• UI Error indeterminate

Harbour has a longer list (http://www.fivetechsoft.com/harbour-docs/harbour.html):

• HB_ISARRAY
• HB_ISBLOCK
• HB_ISCHAR
• HB_ISDATE
• HB_ISDATETIME
• HB_ISHASH
• HB_ISLOGICAL
• HB_ISMEMO
• HB_ISNIL
• HB_ISNULL
• HB_ISNUMERIC
• HB_ISOBJECT
• HB_ISPOINTER
• HB_ISPRINTER
• HB_ISREGEX
• HB_ISSTRING
• HB_ISSYMBOL
• HB_ISTIMESTAMP

ISPOINTER() (http://www.marinas-gui.org/projects/harbour_manual/ispointer.htm) for example is marked: Not available in CA-Cl*pper. Together with HB_IT_POINTER and HB_ISPOINTER it is used in Harbour's internals (the C-level API: http://www.fivetechsoft.com/harbour-docs/clevelapi.html#ispointer).

Let us see a little program that shows the use of the most common data types.

I wanted to show, in this example, a computation of π instead of that of √2, but xBase misses the ArcTan function. We may solve this problem by importing it from an external library, or by supplying it ourselves. (Both ways should be pursued in this tutorial).

The last two data types, are a bit different from the preceding: "Memo" is not very useful when used outside of a database, and the arrays cannot be used in databases.

 && example of compilation command
&& A:\>c:\hb32\bin\hbmk2 -quiet -oc:\hbcode\test.exe test.prg -run

&& please note that this example has not a MAIN function

&& an example of string concatenation
? "Hello" + " " + "World!"

&& let us do now a few numerical computations, integer numbers are a good starting point
? 5+13
? 12*8

? 3/2
SET DECIMALS TO 15
sqrt2=sqrt(2) && computing the square root of 2...
? sqrt2 && ... and printing it

&& ? caporetto
&& if the line above was not commented, the compiler would issue the two following lines
&& Error BASE/1003  Variable does not exist: CAPORETTO
&& Called from TEST(8)

&& as xBase is not good at history, let us revise it: http://www.historyofwar.org/articles/battles_caporetto.html,  http://www.firstworldwar.com/battles/caporetto.htm
&& we can then assign the correct date to this war
caporetto := ctod("10-24-1917")

a := date() && system date
? a + 1 && will print tomorrow's date
? a - caporetto && this will tell us how many days have passed since Caporetto's battle (difference between two dates)

SET DECIMALS TO 2
? (a - caporetto) / 365
?? " years have passed since Caporetto's battle"

? 1+1=3
&& it will print ".F.", that is, "FALSE" (of course...)

&& The following two instructions should be discussed to expand on the subject of operator precedence
? 1/2+2^3*sqr(25)-3^2
? 1/2+2^3*(sqr(25)-3^2)


From the following example (which runs) we see that Harbour is a weakly typed programming language: a variable, such as a in our example can be a number, and then become a string of text.

a := 1+1
? a
a := "abc"
? a


There is a problem: if the variable type changed and the programmer did not get aware of it he may issue some instruction that will cause an error at run time. If we had another line of code

?a+1


in the program above, Harbour will attempt to add the number 1 to the string "abc", and the result will be the error

Error BASE/1081 Argument error: +.

#### Strings

Strings are essentially a list of characters. They are written between double quotes (eg. "Hello"). Note the difference:

123       => a number
"123"     => a string


Let's see how to use the function Stuff() to change a character within a string:

sTest:="fun"
?sTest, STUFF(sTest,2,1,"a")
&& => fun fan


Here is a list of the most commonly used functions for handling strings:

Lower()   Convert uppercase characters to lowercase
Upper()   Converts a character expression to uppercase format
Chr()     Convert an ASCII code to a character value
Asc()     Convert a character to its ASCII value
Len()     Return the length of a character string or the number of elements in an array
At()      Return the position of a substring within a character string


Nevertheless in Clipper and Clipper Tools library can also be found very curious functions such as these:

AsciiSum()   Finds the sum of the ASCII values of all the characters of a string
Soundex()    is a character function that indexes and searches for sound-alike or phonetic matches
CHARONE()    Search and remove repeating characters
CHARONLY()   Remove non-standard characters
CHAROR()     "OR" bytes in a string


#### Other Considerations about the Classification of Data Types

A first classification of data types divides them into simple (or scalar) types and structured (or composite, or aggregate or compound) types.

In xBase the simple data types are numbers, logical values, strings and dates, that is, the types which can be field types in a database table. Memos are not, since they are a pointer to an external file.

Scalar data types can be composed to build up structured data types: arrays, associative arrays (hash tables), tables or databases (which are a "data type" which is stored on a disk). w:Primitive_data_type

Code blocks and objects are special data types.

#### Operators and Precedence

&& First we print the values of two expressions with the exponentiation operator
? 3**3**3 && is 19683
? 3**(3**3) && is 7625597484987

&& Then we let our compiler tell us if they are the same
? 3**3**3 = 3**(3**3) && result is .F.

&& We've a look at how our compiler deals with overflow and underflow...
? 9**9**9
&& ***********************
? 1/9**9**9
&& 0.00
? -(9**9**9)
&& ***********************


We see in this example that the exponentiation operator ** is left-associative (where in mathematics exponentiation is a right-associative operation).

The fact that 3**3**3 and 3**(3**3) give two different results means that parenthesis alter the operator precedence: whatever is enclosed in parenthesis is evaluated first.

Even when the operation is left-associative, the result of 9**9**9 is quite a "large" number (196627050475552913618075908526912116283103450944214766927315415537966391196809 to be precise, as you can easily check - maybe using some arbitrary precision calculator program, such as GNU bc, which is available on every Linux distribution and as a download for Windows at http://gnuwin32.sourceforge.net/packages/calc.htm) which exceeds the resources Harbour allocates for a number (which is called overflow) and it therefore prints 23 times a * to indicate this error. It also gives zero (0.00) if we try to compute its reciprocal (which is underflow). Overflow and underflow errors happen when the result of a computation is, respectively, too big or too small to be represented with the number of bits a compiler has allocated for the variable in which we're trying to store it in.

The same happens with dates. What it the result of

date()-1000000

(subtracting 1 million of days from the current date)? Let us see:

&& today my outputs are:
?date()
&& ==> 05/15/19
?date()-365000 && more or less 1000 years
&& ==> 01/13/20
set century on && we change an option...
?date()-365000 && ... and try againg
&& ==> 01/13/1020
?date()-1000000 && more or less 2700 years (i.e. the results should be around 700 B.C.)
&& ==> 00/00/00
?ctod("31-12-9999")+1
&& ==> 00/00/00


Here, the last two outputs ("00/00/00") indicate invalid dates, just as the rows of 23 asterisks seen above indicate invalid numeric results. Acceptable dates are those that can be stored in a dBase table (ie a .dbf file) and in these files a date is a string of 8 characters in the "YYYYDDMM" format (4 characters for the year, 2 for the month and 2 for the day). Consequently negative years (before Christ) or with more than 5 digits are not accepted.

Harbour displays the year with only two digits unless we change its behaviour with a SET command and gives wrong results with these large number of years. The reason is simple: in a database application (inventory management, reservation systems) we won't have to deal with intervals of centuries and Harbour's routines for handling dates don't work correctly if we try to do that. In other fields, such as w:Archaeoastronomy this is the case and those who do deal with those problems (e.g. checking the precision of the alignment of the Winter solstice sunrise at Karnak in 2000 B.C. or the megalithic alignments) use their special routines for the computation of the dates.

There is another quandary when doing computation with computers. Look:

store 7 to n && an alternate way of an assignment (dates back to dBase II)
?sqrt(sqrt(n))^2^2=n
&& the result is .F.
set decimals to 15
?sqrt(sqrt(n))^2^2
&& 7.000000000000003


Now, if we extract the square root twice from a number and then square the result twice we should have the starting number. In fact computers are not so precise and checking for the equality of two floating point numbers can give unexpected results.

If you want to learn more you could have a look at What Every Computer Scientist Should Know About Floating-Point Arithmetic at https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html (which is part of a Sun Microsystems book entitled Numerical Computation Guide), and the Decimal Arithmetic FAQ at http://speleotrove.com/decimal/decifaq.html.

Fortunately, the precision of the variables used in xBase languages ​​and the fact that in common business applications square roots, logarithms and other mathematical operations that cause loss of precision are rather rare, are circumstances that mitigate the problem.

SET OPTIONS
We've seen SET DECIMALS TO n and SET CENTURY ON/OFF commands. But in fact there are many options which can be set. The SETMODE(<nRows>, <nCols>) function is particularly useful when using hbrun or xbscript: its purpose is to determine the size of the window.

### Boolean Algebra & Flow Control

We can print the result of a comparison, which is a Boolean value (represented as .T. or .F.), but they are useful with special words in order to branch or repeat the execution of one or more instructions.

We have a Boolean expression when its result is true (whose xBase symbol is .T.) or false (.F.). The easiest way to get a Boolean result is to use a comparison (relational) operator: =, ==, !=, <>, #, >, <, >=, and <=. Always remember the difference between the comparison operators = (equal), == (exactly equal) and the assignment operator :=.

Actually the "real" (mathematical) definition of a Boolean expression is different from what is stated in the last paragraph. A "real" Boolean expression would contain only the two Boolean values .T. and .F. and the Boolean operators (.AND., .OR., .NOT.).

Boolean Algebra is applied to electronic digital circuits since the work of Claude Shannon in 1937. See http://www.allaboutcircuits.com/textbook/digital/chpt-7/introduction-boolean-algebra/ (and the successive pages of that online textbook) to have an idea of what Boolean Algebra means when it's applied to bits within a circuit (including a microprocessor). Here we're using it at a much higher level.

Consider Hamlet, Act 3, Scene 1: «[…]To be, or not to be? That is the question— Whether ’tis nobler in the mind to suffer The slings and arrows of outrageous fortune…». If Prince Hamlet asked Boole (but he could not, as it is set in the 14th or 15th century whereas Boole was born in 1815), his reply would have certainly baffled him. Why?

to_be=.T. && is it true?
?to_be .OR. .NOT. to_be
&& .T.
to_be=.T. && is it false?
?to_be .OR. .NOT. to_be
&& .T.


As Harbour told us, in Boolean algebra, the answer is "true". Have a look at this page: https://vicgrout.net/2014/07/18/to-be-or-not-to-be-a-logical-perspective/.

#### Conditional execution

A number of graphical ways have been developed for seeing how a program's flow control is transferred: these include flowcharts (https://www.lucidchart.com/pages/what-is-a-flowchart-tutorial), data flow diagrams (https://www.lucidchart.com/pages/data-flow-diagram), Nassi-Shneiderman diagrams and the JSP and JSD diagrams by Michael A. Jackson (which is almost certainly not the first Michael Jackson you'll think of).

The following example gets a number from the keyboard and prints it. If the number is 0 it comments that value (I presented this example as a typical nihilist programming).

Function MAIN()
LOCAL number
INPUT "Key in a number: " TO number
IF number = 0
?? "Congratulations, you keyed the fabolous number "
ENDIF
? number
RETURN


This example prints two different comments, whether the condition of the remainder of the division of the number supplied by 2 is, or is not, 0.

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


This example shows that more than a command can be executed.

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


This example adds another keyword, ELSEIF, to show that the choices are not necessarily dichotomic. This is a chained conditional. Only one of the branches will be executed.

function MAIN
LOCAL nNumber := 0
//
INPUT "Key in a number: " TO nNumber
//
?? "Your number is "
IF nNumber < 50
? "less than 50"
ELSEIF nNumber = 50
? "equal to 50"
ELSE
? "greater than 50"
ENDIF

   ? Time()
// CToN source is numconv.prg, library is libct.
IF CToN( SubStr( Time(), 1, 2 ) ) < 18
? "Good day"
ELSE
? "Good evening"
ENDIF


Here is a more complex example, coordinating more conditions with .OR. operators. There are three logical operators: .AND., .OR., and .NOT.

   WAIT "Key in a letter: " TO char
IF ( ( char = "a" .OR. char = "A" ) .OR. ( char = "e" .OR. char = "E" ) .OR. ( char = "i" .OR. char = "I" ) .OR. ( char = "o" .OR. char = "O" ) .OR. ( char = "u" .OR. char = "U" ) )
? char, " is a vowel"
ELSE
? char, " is not a vowel"
ENDIF


This same thing could however be achieved in a more concise way using regular expressions.

#### Looping

DO WHILE, EXIT, LOOP, ENDDO are keywords used To repeatedly execute a series of statements (loop body) while a condition is true (i.e. its result is .T.).

Loops come in several flavours: the pre-tested loop, the post-tested loop, and the definite iteration, which is done via a count-controlled loop (usually called a for loop). In practice, we may have a middle-tested loop as well, but no specific syntax for it... we need to put an EXIT statement within an already existing loop body to get this one. 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 number, sum, n
&& An anonymous contributor renamed the variable "num" into "number", increasing this short program readability, but the line above would give
&& Error E0030  Syntax error "syntax error at 'LOCAL'"
Function MAIN
LOCAL number, sum, n
CLS
? "Let's sum up the first n odd numbers."
INPUT "How many numbers shall I sum up? " TO n
sum=0
number=1
DO WHILE number <= 2*n
sum=sum+number
number=number+2
ENDDO
? "The sum requested is ", sum


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 sum=sum+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 programs above is a nice example of how an extremely methodical calculator without any creativity would attack the problem of summing up the first n odd numbers. Notes about the creative approach to it can be found at this address: http://betterexplained.com/articles/techniques-for-adding-the-numbers-1-to-100/ (along with the usual anedocte about Gauss' show off about it in elementary school).

The WHILE looping statement is said to have the control expression in its head,opposed to the Pascal REPEAT-UNTIL looping statement (a post-test loop) which has the control of the condition in its tail (these are the Italian computer science slang for pre- and post- tested loops, aren't they funny?). How does the xBase/Clipper say REPEAT-UNTIL? It doesn't. Here is how to emulate it (copied & pasted from the Norton Guide):

LOCAL lMore := .T.
DO WHILE lMore
loopbody
lMore := condition
ENDDO

As you see, we first set a variable to be true, enter the loop, and specify the condition at the end of the loop, to make sure its body is executed at least once.

Here is an example, equivalent to the one at the pascal_repeat_until_loop page in tutorialspoint.com.

LOCAL a := 10
LOCAL lMore := .T.

DO WHILE lMore
? 'value of a: ', a
a := a + 1
lMore := ( a != 20 )
ENDDO


But the code above changed the condition. If we didn't want this to happen, we can simply negate the condition we'd use in the Pascal code:

repeat loopbody until condition

becomes then:

LOCAL a := 10
LOCAL lMore := .T.

DO WHILE lMore
? 'value of a: ', a
a := a + 1
lMore := .NOT. ( a = 20 ) && or, in alternative, lMore := ! ( a = 20 )
ENDDO


Let us now see this kind of loop:

 DO WHILE .T.
? "I won't stop."
ENDDO


This loop prints "I won't stop." as long as 'true' is... true. It is thus called an infinite loop because the ending condition will never be satisfied. It is the first example of control flaw studied in computer science classes. By definition, you incur in an infinite loop every time you specify a tautology as the condition to be checked. It is not always obvious to detect this and other control flaws and errors - it is indeed a process that involves the use of a specific piece of software called a debugger.

#### Counter-controlled Loops

The FOR...NEXT construct (counter-controlled loop) is useful when we know how many times we want the loop body be executed. We shall now use it to print out and revise multiplication tables.

 CLEAR
FOR I := 1 TO 8
FOR J := 1 TO 8
?? I*J
NEXT
NEXT


(Please note in this example we have a loop inside the body of another loop: this is called a nested loop). Well, this works, but its output is not very nice... Moreover, it is customary to print the multiplication tables up to 10, since we're using a decimal numeration system... let us try it again!

 CLEAR
FOR I := 1 TO 10
FOR J := 1 TO 10
@i,j*4 SAY  I*J PICTURE "999"
NEXT
NEXT


this way we can print it much prettier... by the way, when nesting loops and branching statements, it becomes important (practitioning programmers say so, and in my little I agree) to arrange the indentations in order to make the program easier to read... in The Oasis are available "source code beautifiers" such as dst314.zip. Harbour gives us the hbformat tool, https://vivaclipper.wordpress.com/tag/hbformat/.

PICTURE "999" is the instruction to specify we want the output format - as we specified three digits, the numbers in output will be printed as if they were three digits long, thus aligning them in our multiplication table.

### & - the Macro Evaluation Operator

Consider this short piece of code:

 i = 2
myMacro = "i + 10"
i = &myMacro && will execute "i+10"
? i          && will print "12"


The & is an operator that will evaluate (runtime compilation) an expressions and will permit text substitution within strings (see https://harbour.github.io/ng/c53g01c/ng110bc2.html). Let's make a simple calculator using it.

ACCEPT "Please enter the first number: " TO cNumber1
ACCEPT "Please enter the second number: " TO cNumber2

WAIT "Enter operation: " TO cOperation && we get a single character for the operation

? Number1+cOperation+Number2+"="       && we print the operation and an equal sign
? &(Number1+cOperation+Number2)        && and the result on the following line, evaluating the string as it appeared on the line above


Here is an example of interaction with this program:

Please enter the first number: 12
Please enter the second number: 23.5
Enter operation: -
12-23.5=
-11.5


### Arrays

An array is an ordered grouping of data, whose elements are identified by a number called an index. In many programming languages ​​the indices (or "indexes") start at 0, but in xBase they start from 1. Since in mathematics the indices are written next to the variable slightly below the line, sometimes they are called subscripts and, for this reason, arrays are sometimes called subscripted variables.

Consider this first example about arrays. We create two arrays, one with the names of the months of the year and one with their duration in days, and then we print them in an orderly manner. The same result can be obtained in a much more elegant way using associative arrays.

 && C:\hb32\bin\hbmk2 -quiet -oc:\hbcode\months.exe months.prg -run
DECLARE months [12]
? LEN (months) && the function LEN() shows how many items an array has
&& We shall now load some data into our arrays
PRIVATE month_names: = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
PRIVATE months: = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} && for simplicity we do not consider leap years

&& Let us make some output (using a FOR loop - see below)
FOR I: = 1 TO LEN (months)
? month_names [i], "is", months [i], "days long."
NEXT


In the next example we show that xBase has high-level support for dynamic arrays (implementing them in a low level language like C requires pointer arithmetic and memory management functions)

&& compile with ''hbmk2 averaging.prg -lhbmisc''
PROCEDURE MAIN

LOCAL n, sum := 0, average    && let's initialize the variables
aNumbers := {}                && and an array without elements

? "enter a list of integer numbers, and a non-number when you are finished"
WHILE .T.                     && we begin an endless loop
ACCEPT "next element: " TO item
IF IsDec( item )           && the function IsDec() from the libmisc library checks if we have entered a number
AAdd ( aNumbers, item ) && if we did, then we add the number as a new element to the array
sum := sum + &item      && (and we update the sum of the elements in our array)...
ELSE
EXIT                    && ...if we did not, we quit this loop
ENDIF
ENDDO

average := sum / Len( aNumbers ) && we compute the average of the values in the array

?
? "to average the array you must correct each element by adding to it the corresponding value below"
?
FOR EACH n in aNumbers        && FOR EACH is Harbour syntax
?? AVERAGE - &n
NEXT
RETURN


In addition to a function to add elements to an array we have one to delete them:

 aFruitSalad:={"peaches", "plums"}
AAdd ( aFruitSalad, "onions" ) && we add onions
ADel(aFruitSalad, 3) && on second thought, onions don't mix well: we remove them
local f; for each f in aFruitSalad; ?? f," " ;  next && we print our ingredients
&& => peaches  plums  NIL


Multidimensional arrays are made by nesting arrays within other arrays.

### Functions and Procedures

A function is a piece of code that returns a result every time it is called. Let us consider a function to convert Celsius into Fahrenheit (the same function is provided by libct, but we redo it for educational purposes):

FUNCTION cels2f( tempCelsius )
tempFahrenheit := (tempCelsius*1.8 + 32)
RETURN tempFahrenheit

FUNCTION MAIN()
? cels2f(100)
? cels2f(0)


But have a look at the following:

/**
* by choosing better variable names this function becomes self-explanatory
*/
function convert_Celsius_to_Fahrenheit(Celsius_temperature)
return Celsius_temperature * 1.8 + 32

FUNCTION MAIN()
? convert_Celsius_to_Fahrenheit(100)
? convert_Celsius_to_Fahrenheit(0)


Consider this example of use for the function HB_Random():

PROCEDURE Main
// Random number between 0.01 and 0.99
? HB_Random()
// Random number between 0.01 and 9.99
? HB_Random(10)
// Random number between 8.01 and 9.99
? HB_Random(8,10)
// Random integer number between -100 and 100
? HB_RandomInt(-100,100)
RETURN


A good function would function as a "black box", that is you can use it without worrying about how it internally works. An example usually given at this point is that of driving a car: you can use it without worrying about how the engine works (do you know anybody which looked at the pistons of their new car before buying it?). Someone else designed the engine. But there may be problems: if we change our Celsius into Fahrenheit function as follows

FUNCTION cels2f( tempCelsius )

tempFahrenheit := (tempCelsius*1.8 + 32)
number := number + 1

RETURN tempFahrenheit

FUNCTION MAIN()

number := 0
? cels2f( 100 )
? cels2f( 0 )
? number


we'll get a side effect. This program output is

      212.0
32.0
2


That is, our new function did not only return a value, it also increased by 1 the variable number.

The Clipper Norton Guides read, "A procedure in CA-Clipper is the same as a user-defined function, with the exception that it always returns NIL" (http://www.oohg.org/cl53/ng10a778.html), thus a procedure is really just a type of function that relies on these side effects to get something done.

The utility of functions is that they improve program readability, maintainability. Splitting a program into functions is a good design approach, which lets breaking a complex problem into smaller, simpler problems (top-down).

Harbour has four different kinds of subroutines/subprograms:

• the FUNCTION and
• the PROCEDURE (which is a FUNCTION with no return value),
• the method (which has not an associated keyword, works like every object oriented programming language) and
• the code block.

### Why the Main Function?

The main function is the designated start of a program written in C or C++ (Java uses a static main method). Since a procedure is a function that returns NIL, we can have a procedure main instead of a function main in our program,. With Harbour it is not necessary anymore.

### Another characteristic of variables: scope identifiers

Variables have a name, a type, a scope and a lifetime (http://www.dbase.com/Knowledgebase/dbulletin/bu05vari.htm) which may be explicitly declared as (list below copied verbatim from w:Harbour (software)):

• LOCAL: Visible only within the routine which declared it. Value is lost upon exit of the routine.
• STATIC: Visible only within the routine which declared it. Value is preserved for subsequent invocations of the routine. If a STATIC variable is declared before any Procedure/Function/Method is defined, it has a MODULE scope, and is visible within any routine defined within that same source file, it will maintain its life for the duration of the application lifetime.
• PRIVATE: Visible within the routine which declared it, and all routines called by that routine.
• PUBLIC: Visible by all routines in the same application.

They do not make sense before we work with functions and procedures.

/* Work on the following.
* hbformat was run on this piece of code
* need to provide comments, nest more functions and procedures to help figuring what goes on with scope modifiers
*/

STATIC x := 9

? x
A()
? x
B()
? x
C()
? x
D()
? x
E()
? x

PROCEDURE A

LOCAL x := 10

? "x from A=", x

RETURN

PROCEDURE B

PRIVATE x := 5

? "x from B=", x

RETURN

PROCEDURE C

PUBLIC x := -1

? "x from C=", x

RETURN

PROCEDURE D

? "x from D before updating value=", x

x := 12

? "x from D=", x

RETURN

PROCEDURE E

? "x from E=", x

RETURN


On running this, we get a strange output:

         9
x from A=         10
9
x from B=          5
9
x from C=          -1
9
x from D before updating value=          -1
x from D=         12
9
x from E=         12
9


This program sets a variable x and five different procedures, A, B, C, D, E. The first three procedures define a variable x within themselves, assign it a value and print it. The fourth function assigns a new value to some variable named x without declaring it. The fifth function shows the value of some x variable, which happens to be the value of the fourth x variable. The main fact to remember here is that two variables are not the same, even if hey have the same name, provided they have different scopes and this feature is called shadowing.

x := 9

? x
A()
? x
B()
? x
C()
? x
D()
? x
E()
? x

PROCEDURE A

LOCAL x := 10

? "x from A=", x

RETURN

PROCEDURE B

PRIVATE x := 5

? "x from B=", x

RETURN

PROCEDURE C

PUBLIC x := -1

? "x from C=", x

RETURN

PROCEDURE D

? "x from D before updating value=", x

x := 12

? "x from D=", x

RETURN

PROCEDURE E

? "x from E=", x

RETURN

         9
x from A=         10
9
x from B=          5
9
x from C=         -1
-1
x from D before updating value=         -1
x from D=         12
12
x from E=         12
12


As static variables seem to require further informations (they work like C static variables), here is an example:

// adapted from "An example of static local variable in C" (from Wikipedia Static_variable)
FUNCTION func()
static x := 0
/* x is initialized only once, the first time func() is called */
x := x+1
? x
RETURN

FUNCTION main()
func() // calls func() a first time: it prints 1
func() // value 1 was preserved; this time it prints 2
func() // prints 3
func() // prints 4
func() // prints 5


See pascal_variable_scope in tutorialspoint.com and http://aelinik.free.fr/c/ch14.htm

Variable scope is a foretaste of object oriented programming, in the sense that it anticipates some encapsulation concepts.

### The search for the Ludolphine

#### Part 1: Using the Clipper Tools (CT) Library

If you checked the list of functions you will have noticed that the xBases have no trigonometric functions. Not that in general they miss them: these languages are targeted database applications such as "accounting systems and airline reservations systems" (see w:Database application. ) in which trigonometric functions are not really important. But we now ask ourself how to compute an approximation to the Ludolphine number, π (more informations on this "search" at http://www.mathpages.com/home/kmath457.htm), We can do it using the CT library (see http://vouch.info/harbour/index.html?hbct.htm), or importing the C standard library function atan.

 && compile with ''hbmk2 computepi.prg -lhbct''
PROCEDURE MAIN
SET DECIMAL TO 14
? 4 * ATAN (1)


#### Part 2: Mixing C and Harbour Code

We get the idea from Harbour for beginners by Alexander Kresin (http://www.kresin.ru/en/hrbfaq_3.html).

  #pragma BEGINDUMP
#include <extend.h>
#include <math.h>
HB_FUNC( ATAN )
{
double x = hb_parnd(1);
hb_retnd( atan( x ) );
};
#pragma ENDDUMP
SET DECIMAL TO 14
? 4 * ATAN (1)


It must be noted that the extend.h file (* Compatibility header file for CA-Cl*pper Extend System) contains this warning:

/* DON'T USE THIS FILE FOR NEW HARBOUR C CODE */
/* This file is provided to support some level of */
/* Harbour compatibility for old Clipper C extension code */


In fact this example is here only to consider how the open source cross-compilers (Harbour in this case) extend themselves with C library functions, which gives them a lot of flexibility. Not that computing π would be useful in itself for for the average xBase programmer (by the way the CT library contains a function to directly compute π). We shall not concern ourselves with a method to do that, but move to something more conventional.

### Sorting Algorithms

Sorting data is an important application in computer science. In fact, in the ancient times (which means in the 50s), an important component of a computer was a piece of hardware called a Card Sorter, which was used to sort punched cards, see one at http://www.columbia.edu/cu/computinghistory/sorter.html, but the Census Machine built by Hollerith in the 1880's already had its own sorter box, and the French word for computer, ordinateur, can even be literally translated to sorter.

   // we'll declare and load random numbers from 1 to 100 in an array
DECLARE arrayToSort [12]
FOR I = 1 TO 12
arrayToSort [I] = Int( HB_Random( 1,101 ) )
NEXT

// we review our array
FOR I = 1 TO 12
?? arrayToSort [I]
NEXT

// we'll do Exchange Sort as described in http://www.ee.ryerson.ca/~courses/coe428/sorting/exchangesort.html
// where there is also a nice animation showing how it works! :)
for i := 2 TO Len(arrayToSort)
for j := Len(arrayToSort) TO i step -1
IF arrayToSort [j - 1] > arrayToSort [j]
TEMP := arrayToSort [j - 1]
arrayToSort [j - 1] := arrayToSort [j]
arrayToSort [j] := TEMP
ENDIF
NEXT
NEXT

?
? "Sorted array:"
?

// we look at our sorted array
FOR I = 1 TO 12
?? arrayToSort [I]
NEXT


From the version above we will separate the sorting routine in a procedure

PROCEDURE ExchangeSort(array)
// we'll do Exchange Sort as described in http://www.ee.ryerson.ca/~courses/coe428/sorting/exchangesort.html
// where there is also a nice animation showing how it works! :)
for i := 2 TO Len(array)
for j := Len(array) TO i step -1
IF array [j - 1] > array [j]
TEMP := array [j - 1]
array [j - 1] := array [j]
array [j] := TEMP
ENDIF
NEXT
NEXT
RETURN

FUNCTION MAIN
// we'll declare and load random numbers from 1 to 100 in an array
DECLARE arrayToSort [12]
FOR I = 1 TO 12
arrayToSort [I] = Int( HB_Random( 1,101 ) )
NEXT

// we review our array
FOR I = 1 TO 12
?? arrayToSort [I]
NEXT

ExchangeSort(arrayToSort) // we call the procedure and pass to it our array called arrayToSort

?
? "Sorted array:"
?

// we show the array is sorted
FOR I = 1 TO 12
?? arrayToSort [I]
NEXT


Here is our basis. We'll add a flag variable to improve this algorithm.

### Code blocks

Sites around the Net says that Clipper 5 documentation called them unnamed assignable functions.

Let us get back at our Celsius to Fahrenheit temperature converting function:

FUNCTION cels2f( tempCelsius )
tempFahrenheit := (tempCelsius*1.8 + 32)
RETURN tempFahrenheit


and redo it as a codeblock:

Cels2F := { | celsius | Fahrenheit := (Celsius * 1.8 + 32) } // our first codeblock

? Eval(Cels2F,100)
? Eval(Cels2F,0)


In the first line we assigned a code block to a variable named Cels2F. The code block is comprised in braces. The first thing is a variable that is to be passed to the block, between vertical bars (|). More variables should be separated by commas. Then we can write the instructions (more than one should be separated by commas).

Later we use two times the Eval function giving it two arguments: the name we assigned to our codeblock and the parameter to pass it. In addition to Eval, other functions can evaluate code block: AEval() and dbEval(). Ascan() and Asort() can be given code blocks to change their behavior.

As code blocks can't contain commands, we can't put a ? command in them, but have to use the corresponding QOut() function.

### Command Line Arguments

Suppose that we wish to write a clone of the Unix cat command, which means that we will have a program to which we'll pass a command line argument that is a file name and output the content of the file.

PROCEDURE MAIN(file)
? MEMOREAD(file)
RETURN


That's it! In fact it is quite limited, and it is more likely a clone of the DOS type command (or even of the CP/M type command!). Procedure This file's procedure MAIN receives one argument, and gets it from the command line. Then passes it to the MEMOREAD function (description: Return the text file’s contents as a character string), and sends the result to the console output.

### File Management

Accessing files can be done using low-level functions, which are: FOPEN(), FCREATE(), FWRITE(), FREAD(), FREASDSTR(), FERASE(), FERROR(), FSEEK(), FCLOSE() and FRENAME().

Harbour however, provides functions that permit manipulating text files in a similar way to DBF files (https://vivaclipper.wordpress.com/tag/memoread/).

## Working with Databases

Let's return to the Wikipedia entry Database application.

Different kinds of database applications exists as well. If you did store your friend's phone numbers and addresses into a word processor, you would have what someone calls a Free-Form Database (however, a similar expression is an oxymoron in computer science) - myBase®, askSam®, Personal Knowbase®, MyInfo®, Info Select® and GeneralKB® are a bunch of specialized free-form database application, which actually means PIM (Personal information manager). Now, a word processor lets us search the information, but other operations, such as sorting them, cannot be done automatically by a word processor.

What about attempting to store it into a spreadsheet? We may use one column for the name, one for the surname, one for the telephone number, one for the city. This quick database, stored in a spreadsheet, may be searched and sorted: for example we can sort it by city and person's name in alphabetical order. This is a flat database, http://www2.research.att.com/~gsf/man/man1/cql.html: a flat database is a sequence of newline terminated records of delimiter separated fields, and a spreadsheet shows its limits in data entry and reporting (if you did want to use the data in your table to print out addresses on envelopes a spreadsheet is not a good tool). An example is MyDatabase (http://www.pcmag.com/article2/0,2817,760833,00.asp).

Spreadsheets are much better to do accounting: how much harder a book-keeper's work would be if his data were stored in a wordprocessing program? The purpose here is to have our data structured in a certain way: all the costs in a place, all earnings in another.

Before 1970 complex databases where managed using Hierarchical Databases (very little information is needed about them - see for example http://www.extropia.com/tutorials/sql/hierarchical_databases.html and http://people.cs.pitt.edu/~chang/156/14hier.html). An example of a hierarchical database is IBM IMS (Information Management System, which was developed during the mid-1960s for applications in the aerospace industry). Hierarchical Databases and Network Databases together form what today are referred to as Legacy Database Systems. Network databases where born as an extension to the programming language COBOL by the Conference on Data Systems Languages (CODASYL).

Today's standard is the Relational Database (RDBMS), which is "a database with relationships between more than one table of records based on common fields". We will speak of them in some detail, but we will briefly mention the fourth approach: Object Oriented Databases. These databases store objects (in the same sense the word is used in the expression object oriented programming). They're not much used, mostly because object are more complex than the simple fields a relational database stores in its tables. Informations at http://www.odbms.org/odmg-standard/.

The Wikipedia entry about DBase reads: «dBase is application development language and integrated navigational database management system which Ashton-Tate labeled as "relational" but it did not meet the criteria defined by Dr. Edgar F. Codd's relational model». Codd's criteria (the 12 rules, which really are 13 because the first one is numbered '0') are so strict that that it is very hard to find a true relational database system, but the point is that dBase accessed databases in another way, so that it's considered a Navigational Database (which works in a way that simulates relational databases).

### Making a first database and recording some data

#### A verbose way

 && it was done this way at the Dot Prompt
&& we can type this interactively in hbrun
CREATE TMPNAMES
USE TMPNAMES
APPEND BLANK
REPLACE FIELD_NAME WITH "NAME"
REPLACE FIELD_TYPE WITH "C"
REPLACE FIELD_LEN WITH 15
APPEND BLANK
REPLACE FIELD_NAME WITH "ADDRESS"
REPLACE FIELD_TYPE WITH "C"
REPLACE FIELD_LEN WITH 30
CLOSE
CREATE NAMES FROM TMPNAMES && https://www.itlnet.net/programming/program/Reference/c53g01c/ngc785e.html
ERASE TMPNAMES.DBF && we get rid of the temporary file


The code above created a DBF file, names.dbf, to be used by the following code. It will add a record to the DBF file. It is equivalent to the "First Sample Program" of my old PC GUIDE, which missed a line that is necessary in modern xBase.

 CLEAR
? "First Sample Program"
SELECT 1
USE NAMES
APPEND BLANK
REPLACE NAME WITH "MIKE BROWN"
REPLACE ADDRESS WITH "ROME STREET, 56"
CLOSE && this line is missing in my PC GUIDE but is needed in a compiled Harbour program
QUIT


The CLOSE command is equivalent to the dbCloseArea() function, which closes a work area: Pending updates are written, pending locks are released.

#### A more concise way

The short code below does the same work of the two pieces of code of the previous section (it only produces a different file name, namesdb.dbf instead of names.dbf).

 local aStruct := { { "NAME", "C", 15, 0 }, ;
{ "ADDRESS",   "C",  30, 0 }}
REQUEST DBFCDX
dbCreate( "namesdb", aStruct, "DBFCDX", .t., "NAMESDB" )
&& http://www.fivetechsoft.com/harbour-docs/api.html
USE NAMESDB
NAMESDB->(DbAppend())
NAMESDB->NAME := "MIKE BROWN"
NAMESDB->ADDRESS := "ROME STREET, 56"


This example uses the alias operator, ->. http://www.ousob.com/ng/clguide/ngcf412.php

The alias->field_name notation is used to allow access to fields of databases that are loaded but not active. The alias can be specified with the work area number (e.g. 2->std_id), with the work area alias (e.g. B->std_id), or with the database name (e.g. STUDENTS->std_id).

The result of this code is a file named namesdb.dbf. Informations about how DBF files are can be find at DBF File structure, http://www.dbf2002.com/dbf-file-format.html, where we find this list of Field type:

• C – Character
• Y – Currency
• N – Numeric
• F – Float
• D – Date
• T – DateTime
• B – Double
• I – Integer
• L – Logical
• M – Memo
• G – General
• C – Character (binary)
• M – Memo (binary)
• P – Picture
• + – Autoincrement (dBase Level 7)
• O – Double (dBase Level 7)
• @ – Timestamp (dBase Level 7)

My PC GUIDE showed how a .dbf file is made with the DataBase Utility DBU. Clones of this utility are FiveDBU (with source code) at https://code.google.com/archive/p/fivewin-contributions/downloads, DBF Viewer Plus at http://www.alexnolan.net/software/dbf.htm, CLUT at http://www.scovetta.com/archives/simtelnet/msdos/clipper. Harbour includes its own HbDBU (the source is in \hb32\addons\hbdbu) and a component IdeDBU of HbIDE (the other two components are IdeEDITOR and IdeREPORTS).

From https://code.google.com/archive/p/fivewin-contributions/downloads we can get fivedbu_20130930.zip (New FiveDBU version with enhancements on ADO fields editing). It supports ADO, 3 RDD (DBFNTX, CBFCDX and RDDADS) and 6 languages - select "Bases de datos -> Preferencias -> Lenguaje: Inglés" to have it in English.

Let us see what is in our little file so far.

 USE NAMES
LIST DATE(), TIME(), NAME, ADDRESS


### Database Design Issue: the First Normal Form (1NF)

The work done in the previous section was intended to exactly reproduce the database presented in my PC GUIDE. There are, however, drawbacks: having only one NAME field, this database cannot sort its data on the last name. Also, a careless user might insert the data of some people with the last name first, and some other data with the first name last. When designing a database precautions should be taken of these possibilities. The first normal form (http://www.1keydata.com/database-normalization/first-normal-form-1nf.php, http://www.sqa.org.uk/e-learning/SoftDevRDS02CD/page_14.htm) requires you to define fields whose information cannot be divided into smaller parts. So, instead of a NAME field, we should have a FIRST_NAME and LAST_NAME fields. Complying to the first normal form, our little database would be on the on the right track to being a normalized database.

Designing the database is an essential part of the work and it is not always obvious how it should be done. See https://www.ntu.edu.sg/home/ehchua/programming/sql/relational_database_design.html.

A graphical device used to help is database design are the Entity Relationship Diagrams: https://www.lucidchart.com/pages/er-diagrams, https://www.guru99.com/er-diagram-tutorial-dbms.html.

### Complicating our simple database

Harbour contains a file named test.dbf. Launch hbrun in its directory and type in

use test
browse()


At this point we see that it is a 500 record table. Move around with the cursor keys and, when you're finished, punch the Esc key to quit this interactive table browser and editor. To get the record number of a person called Ted issue:

locate for first="Ted"
? recno()


Here is the testdbf.prg source from \hb30\tests. It should be discussed in detail. It is a GPL piece of code poorly commented.

 /*
* $Id: testdbf.prg 1792 1999-11-10 10:17:19Z bcantero$
*/

function main()

local nI, aStruct := { { "CHARACTER", "C", 25, 0 }, ;
{ "NUMERIC",   "N",  8, 0 }, ;
{ "DOUBLE",    "N",  8, 2 }, ;
{ "DATE",      "D",  8, 0 }, ;
{ "LOGICAL",   "L",  1, 0 }, ;
{ "MEMO1",     "M", 10, 0 }, ;
{ "MEMO2",     "M", 10, 0 } }

REQUEST DBFCDX

dbCreate( "testdbf", aStruct, "DBFCDX", .t., "MYALIAS" )

? "[" + MYALIAS->MEMO1 + "]"
? "[" + MYALIAS->MEMO2 + "]"
? "-"
MYALIAS->( dbAppend() )
MYALIAS->MEMO1 := "Hello world!"
MYALIAS->MEMO2 := "Harbour power"
? "[" + MYALIAS->MEMO1 + "]"
? "[" + MYALIAS->MEMO2 + "]"
MYALIAS->( dbAppend() )
MYALIAS->MEMO1 := "This is a test for field MEMO1."
MYALIAS->MEMO2 := "This is a test for field MEMO2."
? "[" + MYALIAS->MEMO1 + "]"
? "[" + MYALIAS->MEMO2 + "]"
MYALIAS->NUMERIC := 90
MYALIAS->DOUBLE := 120.138
? "[" + Str( MYALIAS->DOUBLE ) + "]"
? "[" + Str( MYALIAS->NUMERIC ) + "]"

? ""
? "Press any key..."
InKey( 0 )

? ""
? "Append 50 records with memos..."
for nI := 1 to 50
MYALIAS->( dbAppend() )
MYALIAS->MEMO1 := "This is a very long string. " + ;
"This may seem silly however strings like this are still " + ;
"used. Not by good programmers though, but I've seen " + ;
"stuff like this used for Copyright messages and other " + ;
"long text. What is the point to all of this you'd say. " + ;
"Well I am coming to the point right now, the constant " + ;
"string is limited to 256 characters and this string is " + ;
"a lot bigger. Do you get my drift ? If there is somebody " + ;
"who has read this line upto the very end: Esto es un " + ;
"sombrero grande rid¡culo." + Chr( 13 ) + Chr( 10 ) + ;
"/" + Chr( 13 ) + Chr( 10 ) + "[;-)" + Chr( 13 ) + Chr( 10 )+ ;
"\"
next
MYALIAS->( dbCommit() )

? "Records before ZAP:", MYALIAS->( LastRec() )
? "Size of files (data and memo):", Directory( "testdbf.dbf" )[1][2], ;
Directory( "testdbf.fpt" )[1][2]
MYALIAS->( __dbZap() )
MYALIAS->( dbCommit() )
? "Records after ZAP:", MYALIAS->( LastRec() )
? "Size of files (data and memo):", Directory( "testdbf.dbf" )[1][2], ;
Directory( "testdbf.fpt" )[1][2]
? "Value of fields MEMO1, MEMO2, DOUBLE and NUMERIC:"
? "[" + MYALIAS->MEMO1 + "]"
? "[" + MYALIAS->MEMO2 + "]"
? "[" + Str( MYALIAS->DOUBLE ) + "]"
? "[" + Str( MYALIAS->NUMERIC ) + "]"
? "Press any key..."
InKey( 0 )
dbCloseAll()

dbCreate( "testdbf", aStruct,, .t., "MYALIAS" )

for nI := 1 to 10
MYALIAS->( dbAppend() )
MYALIAS->NUMERIC := nI
? "Adding a record", nI
if nI == 3 .or. nI == 7
MYALIAS->( dbDelete() )
? "Deleting record", nI
endif
next
MYALIAS->( dbCommit() )

? ""
? "With SET DELETED OFF"
? "Press any key..."
InKey( 0 )

MYALIAS->( dbGoTop() )
do while !MYALIAS->( Eof() )
? MYALIAS->NUMERIC
MYALIAS->( dbSkip() )
enddo

SET DELETED ON
? ""
? "With SET DELETED ON"
? "Press any key..."
InKey( 0 )

MYALIAS->( dbGoTop() )
do while !MYALIAS->( Eof() )
? MYALIAS->NUMERIC
MYALIAS->( dbSkip() )
enddo

? ""
? "With SET DELETED ON"
? "and  SET FILTER TO MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8"
? "Press any key..."
InKey( 0 )

MYALIAS->( dbSetFilter( { || MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8 }, ;
"MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8" ) )
MYALIAS->( dbGoTop() )
do while !MYALIAS->( Eof() )
? MYALIAS->NUMERIC
MYALIAS->( dbSkip() )
enddo

SET DELETED OFF
? ""
? "With SET DELETED OFF"
? "and  SET FILTER TO MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8"
? "Press any key..."
InKey( 0 )

MYALIAS->( dbSetFilter( { || MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8 }, ;
"MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8" ) )
MYALIAS->( dbGoTop() )
do while !MYALIAS->( Eof() )
? MYALIAS->NUMERIC
MYALIAS->( dbSkip() )
enddo

? "dbFilter() => " + dbFilter()
? ""

? "Testing __dbPack()"
? "Records before PACK:", MYALIAS->( LastRec() )
? "Size of files (data and memo):", Directory( "testdbf.dbf" )[1][2], ;
Directory( "testdbf.dbt" )[1][2]
SET FILTER TO
MYALIAS->( __dbPack() )
MYALIAS->( dbCommit() )
? "Records after PACK:", MYALIAS->( LastRec() )
? "Size of files (data and memo):", Directory( "testdbf.dbf" )[1][2], ;
Directory( "testdbf.dbt" )[1][2]
? "Press any key..."
InKey( 0 )
? "Value of fields:"
MYALIAS->( dbGoTop() )
do while !MYALIAS->( Eof() )
? MYALIAS->NUMERIC
MYALIAS->( dbSkip() )
enddo
? ""

? "Open test.dbf and LOCATE FOR TESTDBF->SALARY > 145000"
? "Press any key..."
InKey( 0 )
dbUseArea( ,, "test", "TESTDBF" )
locate for TESTDBF->SALARY > 145000
do while TESTDBF->( Found() )
? TESTDBF->FIRST, TESTDBF->LAST, TESTDBF->SALARY
continue
enddo
? ""
? "LOCATE FOR TESTDBF->MARRIED .AND. TESTDBF->FIRST > 'S'"
? "Press any key..."
InKey( 0 )
dbUseArea( ,, "test", "TESTDBF" )
locate for TESTDBF->MARRIED .AND. TESTDBF->FIRST > 'S'
do while TESTDBF->( Found() )
? TESTDBF->FIRST, TESTDBF->LAST, TESTDBF->MARRIED
continue
enddo

return nil


### Input Mask

A simple data base input mask (from the Wikipedia Clipper entry):

USE Customer SHARED NEW
clear
@  1, 0 SAY "CustNum" GET Customer->CustNum PICT "999999" VALID Customer->CustNum > 0
@  3, 0 SAY "Contact" GET Customer->Contact VALID !empty(Customer->Contact)
@  4, 0 SAY "Address" GET Customer->Address
READ


### Deleting records

? LASTREC()
DELETE RECORD 4
PACK
? LASTREC()


In this piece of code the command DELETE marks the fourth record for deletion. But the file is not altered, not even by a CLOSE command. The PACK command actually removes the records marked for deletion (and also makes some additional work). The RECALL command removes the deleted flags. The function DELETED() returns .T. if the current record is marked for deletion, .F. if not.

The PACK command, which does the actual deletion of data from the table, PACK requires that the current database be USEd EXCLUSIVEly. If this condition is not met when PACK is invoked, CA-Clipper generates a runtime error. Additional work that PACK does is to update indexes on the table it alters (if any).

The commands DELETE ALL and PACK are executed by a single command called ZAP.

     &&  This example demonstrates a typical ZAP operation in a network
&&   environment:

USE Sales EXCLUSIVE NEW
IF !NETERR()
SET INDEX TO Sales, Branch, Salesman
ZAP
CLOSE Sales
ELSE
? "Zap operation failed"
BREAK
ENDIF


### An Indexed Example

USE Clients NEW
INDEX ON Name TO Clients UNIQUE


Suppose a table containing these data:

FSTNAME	LSTNAME
John   	Doe
John   	Doe
John   	Doe
Jane   	Doe


We can create a little index file with this piece of code:

SELECT 1
USE ind
? FILE("ind.ntx")
INDEX ON FstName TO ind
? FILE("ind.ntx") // we verify that a NTX file has been created


## Other programming topics

### Hash Arrays (Associative Arrays, Also Known as "Hash Tables")

In an associative array the elements are accessed using a string and not a number as in the "normal" array.

There are online "Calorie checkers" (which are also a nice idea for a web application for the last part) and I think it will be fun to make us a very small one (BEWARE: the information obtained from this program should not replace a varied, balanced diet and a healthy lifestyle).

   hFood := { => }
hFood["Cornflakes"] := "370"
hFood["Spaghetti"] := "101"
hFood["Beef"] := "280"
hFood["Ham"] := "240"
hFood["Broccoli"] := "32"
hFood["Grapefruit"] := "32"
hFood["Spinach"] := "8"
hFood["Chocolate"] := "500"
ACCEPT "Enter a food: " TO cFood
IF hb_HHasKey( hFood, cFood )
? "&cFood is " + hFood[cFood] + " calories per 100 grams"
ELSE
? "&cFood: information not available"
ENDIF

Now we will redo the example of the array of months, showing an alternative syntax for loading data into an hash array:

LOCAL month
hMonths := { "January" => 31, "February" => 28, "March" => 30,  "April" => 30, "May" => 31, "June" => 30, "July" => 31, "August" => 31, "September" => 30, "October" => 31, "November" => 30, "December" => 31 }

FOR EACH month in hMonths
? month:__ENUMKEY()
?? month:__ENUMVALUE()
NEXT


hb_HEval() is the AEval() equivalent for hash arrays.

#### Morse Code

Let us try to convert a text to Morse code: to begin we load the chart of Morse code in an associative array and define a code block which uses the function TONE() to sound a speaker tone for a dot or a dash - employing the IF() function - which is essentially the same of a spreadsheet IF() function - to use the right duration tone.

Then we prompts the user for a message, if none is supplied it uses the pangram "The quick brown fox jumps over a lazy dog" (a pangram is a sentence that contains all of the letters of the alphabet).

We show what message we will translate and starts two a for each loop: the first will scan the message showing which letter it will translate, one at a line (and will also use TONE() with zero frequency and appropriate lengths to make the pauses between letters and words) and then a nested for each loop will show whether it is about to sound a dot or a dash, running the code block to have the right sound.

LOCAL letter, ditdah
hMorseCode := { => }

hMorseCode[ "a" ] := ".-"
hMorseCode[ "b" ] := "-..."
hMorseCode[ "c" ] := "-.-."
hMorseCode[ "d" ] := "-.."
hMorseCode[ "e" ] := "."
hMorseCode[ "f" ] := "..-."
hMorseCode[ "g" ] := "--."
hMorseCode[ "h" ] := "...."
hMorseCode[ "i" ] := ".."
hMorseCode[ "j" ] := ".---"
hMorseCode[ "k" ] := "-.-"
hMorseCode[ "l" ] := ".-.."
hMorseCode[ "m" ] := "--"
hMorseCode[ "n" ] := "-."
hMorseCode[ "o" ] := "---"
hMorseCode[ "p" ] := ".--."
hMorseCode[ "q" ] := "--.-"
hMorseCode[ "r" ] := ".-."
hMorseCode[ "s" ] := "..."
hMorseCode[ "t" ] := "-"
hMorseCode[ "u" ] := "..-"
hMorseCode[ "v" ] := "...-"
hMorseCode[ "w" ] := ".--"
hMorseCode[ "x" ] := "-..-"
hMorseCode[ "y" ] := "-.--"
hMorseCode[ "z" ] := "--.."
hMorseCode[ "0" ] := "-----"
hMorseCode[ "1" ] := ".----"
hMorseCode[ "2" ] := "..---"
hMorseCode[ "3" ] := "...--"
hMorseCode[ "4" ] := "....-"
hMorseCode[ "5" ] := "....."
hMorseCode[ "6" ] := "-...."
hMorseCode[ "7" ] := "--..."
hMorseCode[ "8" ] := "---.."
hMorseCode[ "9" ] := "----."

nDotDuration := 4 && The dot duration is the basic unit of time measurement in Morse code transmission and we set it to 4/18 seconds
bPlayMorse := {| cCurrent| if ( cCurrent = ".", Tone( 480, nDotDuration ), Tone( 480, nDotDuration * 3 ) ) }

ACCEPT "Enter message: " TO message
IF message == "" && if no message we use a default one
message := "The quick brown fox jumps over a lazy dog" && an English-language pangram — a sentence that contains all of the letters of the alphabet
ENDIF

? "Converting '"+Upper( message )+"' in Morse..."

FOR EACH letter in Lower( message )
? Upper(letter)+" "
IF letter==" "
Tone( 0, nDotDuration * 7 ) && a long pause between words...
ELSE
FOR EACH ditdah in hMorseCode[ letter ]
?? ditdah
Eval( bPlayMorse, ditdah )
NEXT
ENDIF
Tone( 0, nDotDuration * 3 ) && ... and a short pause between letters
NEXT


### Regular Expressions

Regular expressions can be considered an extension of wildcards. In a DOS or Windows prompt, for example, we could list all dbf files in the current directory with the command

dir *.dbf


where the asterisk, as they say, matches one or more characters. The other wildcard, the question mark, will match any character, but exactly one: the commands

dir ?.dbf
dir ??.dbf


will show, respectively, every dbf file in the current directory whose name is exactly one or two characters long.

Regular expressions are much more flexible. They were invented by the mathematician Stephen Cole Kleene (the asterisk wildcard derives from an operator he defined and which is called Kleene star). Their flexibility allows us to write an expression that can match dbf filenames one or two characters long in a single expression which looks like this:

.{1,2}\.dbf


There are different types of regular expressions: basic, extended, PCRE (Perl-compatible regular expressions, http://www.pcre.org/), and many other different implementations.

As far as we are concerned we can limit ourselves to PCRE and refer to the documentation at these URLs: https://github.com/Petewg/harbour-core/wiki/Regular-Expressions and https://github.com/zgamero/sandbox/wiki/X_RegularExpressions, https://www.pcre.org/original/doc/html/pcrepattern.html, https://www.debuggex.com/cheatsheet/regex/pcre.

Here https://www.rexegg.com/regex-cookbook.html is a cookbook to see examples and here https://www.regexpal.com/ and here https://regexr.com/ there are interactive tools to try online (and interactively) expressions.

As a first example we will redo the program that receives a letter from the keyboard and reports if it is a vowel or not.

LOCAL cRegEx := "[aeiou]"
WAIT "Key in a letter: " TO char
? char + " is" + iif( hb_regexLike( cRegEx, char, .F. ), "", " not" ) + " a vowel."


The synopsis of hb_RegExLike is this: hb_RegExLike(<hRegEx>|<cRegEx>, <cText> [, <lCaseSensitive>] [, <lMultiLine>]).

## Debugging

You may have heard or read the legend that the term bug appeared when an actual bug was found in the Harvard's Mark II computer. According to one of my books from the high school, it was a little butterfly found in the ENIAC!

Text books less prone to folklore call this a story without foundation, for example

https://www.computerworld.com/article/2515435/moth-in-the-machine--debugging-the-origins-of--bug-.html states that Thomas Edison used the word already in 1878. The same goes with the Wikipedia entry →Software bug which even includes a picture of the moth actually found in the Harvard Mark II and describes the whole history.

A page from the →Harvard Mark II electromechanical computer's log, featuring a dead moth that was removed from the device.

## Object Oriented Programming

### Looking at Object-Oriented Programming from a Safety Distance

Let us suppose we're dealing with the distance function given the Cartesian coordinates of the points (http://mathinsight.org/cartesian_coordinates). The formulas we'll apply are:

${\displaystyle D_{1}={\sqrt {(x_{2}-x_{1})^{2}}}}$

${\displaystyle D_{2}={\sqrt {(x_{2}-x_{1})^{2}+(y_{2}-y_{1})^{2}}}}$

${\displaystyle D_{3}={\sqrt {(x_{2}-x_{1})^{2}+(y_{2}-y_{1})^{2}+(z_{2}-z_{1})^{2}}}}$

for (Euclidean) distance on a real line, Euclidean plane and Euclidean space respectively.

In procedural programming our functions would look like this:

? distance1d(4,-3)
? distance2d(2,-3,-1,-2)
? distance3d(1,1,1,4,4,4)

FUNCTION distance1d( x1, x2 )
RETURN sqrt((x2-x1)^2)

FUNCTION distance2d( x1,y1,x2,y2 )
RETURN sqrt((x2-x1)^2+(y2-y1)^2)

FUNCTION distance3d( x1,y1,z1,x2,y2,z2 )
RETURN sqrt((x1-x2)^2+(y1-y2)^2+(z1-z2)^2)


We defined three functions, with different names, which take as arguments the coordinates. But doing so we need to pass six arguments for the distance in a three-dimensional space.

If we're doing it with object oriented programming we may get something like this:

#include "hbclass.ch"

CREATE CLASS Point1D

VAR Abscissa    // the abscissa of our point

METHOD New( Abscissa )    // Constructor

METHOD Distance( Point )

ENDCLASS

CREATE CLASS Point2D INHERIT Point1D

VAR Ordinate    // the ordinate of our point

METHOD New( Abscissa, Ordinate )    // Constructor

METHOD Distance( Point )

ENDCLASS

CREATE CLASS Point3D INHERIT Point2D

VAR Zcoord    // the Z-coordinate of our point

METHOD New( Zcoord )    // Constructor

METHOD Distance( Point )

ENDCLASS

&& Constructors Zone
METHOD New( Abscissa ) CLASS Point1D

::Abscissa := Abscissa

RETURN Self

METHOD New( Abscissa, Ordinate ) CLASS Point2D

::Abscissa := Abscissa
::Ordinate := Ordinate

RETURN Self

METHOD New( Abscissa, Ordinate, Zcoord ) CLASS Point3D

::Abscissa := Abscissa
::Ordinate := Ordinate
::Zcoord := Zcoord

RETURN Self

&&Distances Methods

METHOD Distance( Point ) CLASS Point1D

RETURN Sqrt( ( Self:Abscissa - Point:Abscissa ) ^ 2 )

METHOD Distance( Point ) CLASS Point2D

RETURN Sqrt( ( Self:Abscissa - Point:Abscissa ) ^ 2 + ( Self:Ordinate - Point:Ordinate ) ^ 2 )

METHOD Distance( Point ) CLASS Point3D

RETURN Sqrt( ( Self:Abscissa - Point:Abscissa ) ^ 2 + ( Self:Ordinate - Point:Ordinate ) ^ 2 + ( Self:Zcoord - Point:Zcoord ) ^ 2 )

PROCEDURE Main()

FirstPoint := Point1D():New( 3 )
SecondPoint := Point1D():New( - 3 )
? FirstPoint:Abscissa
? FirstPoint:Distance( SecondPoint )
ThirdPoint := Point2D():New( 2, - 3 )
FourthPoint := Point2D():New( - 1, - 2 )
? ThirdPoint:Distance( FourthPoint )
FifthPoint := Point3D():New( 1, 1, 1 )
SixthPoint := Point3D():New( 4, 4, 4 )
? FifthPoint:Distance( SixthPoint )

RETURN


Here we've defined three classes, their constructors, and a distance method for each of them, and showed how to use them. It is also a simple example of how inheritance works. Other concepts are encapsulation (information hiding or data hiding), abstraction, polymorphism, overloading and overriding of methods. Inheritance plays a central role in a key concept: reuse. A class can be reused in a software project if it is exactly what was needed; or if it is not exactly what was needed it can be extended by defining a subclass. Much like the design of a database, the design of object oriented class is an art with its principles, see for example http://www.oodesign.com/, http://www.codeproject.com/articles/567768/object-oriented-design-principles and pages about UML like http://www.uml-diagrams.org/uml-object-oriented-concepts.html.

The first thing to note is that we start by including the clipper header file hbclass.ch, the header file for Class commands. This means that object oriented commands have been added to the xBase foundation of Harbour.

Access to variables and methods of an object is done via the colon operator. A prepended double colon refers to variables with a larger scope (such as those passed to a method).

In the code above we defined three classes, each one implementing a Point. Point2D for example was defined as a class extending Point1D, that is a generalization of the concept. A method Distance was given for each of the classes.

A line such as

  ? FifthPoint:Distance( SixthPoint )


contain the output command ?, the reference to an object (FifthPoint in this case), an invocation of the Distance method :Distance, to which another point was passed ( SixthPoint ).

It is also possible to write a Distance function which takes two arguments of a Point class, that may look like this:

FUNCTION Distance ( Point1, Point2 )
RETURN Sqrt( ( Point1:Abscissa - Point2:Abscissa ) ^ 2 + ( Point1:Ordinate - Point2:Ordinate ) ^ 2 + ( Point1:Zcoord - Point2:Zcoord ) ^ 2 )

? Distance( FifthPoint, SixthPoint )


This is, however, not object-oriented programming, as we could have written the same function with a not object-oriented language such as Pascal or C, passing it two structs, or records as Pascal calls them, named Point1 and Point2.

It is important the fact that some data internal to the object (set by the real programmer of the thing) can't be changed by the object user. In real life an example is that of a car engine. The provider of the object set a number of cylinders, and we have not many chances of changing that: we've got to regard it as a constant. There is naturally a number of interesting formulas about engines that engineers use (some to be seen at http://www.thecartech.com/subjects/engine/engine_formulas.htm). For example the one for computing the Engine Volumetric Efficiency given the volume of air taken into a cylinder and the cylinder swept volume. Here comes the importance of data hiding: nobody needs to know those informations to get his car going. Also, when someone designes an engine they probably don't expect the user to change the volumetric efficiency by operating on the engine. The same thing is obtained in object oriented programming using visibility modifiers, or access modifiers.

   [CREATE] CLASS <cClassName> [ FROM | INHERIT <cSuperClass1> [, ... ,<cSuperClassN>] ]
[ MODULE FRIENDLY ] [ STATIC ] [ FUNCTION <cFuncName> ]

[HIDDEN:]
[ CLASSDATA | CLASSVAR  | CLASS VAR <DataName1>]
[ DATA | VAR  <DataName1> [,<DataNameN>] [ AS <type> ] [ INIT <uValue> ]
[[EXPORTED | VISIBLE] | [PROTECTED] | [HIDDEN]] [READONLY | RO] ]
...
[ METHOD <MethodName>( [<params,...>] ) [CONSTRUCTOR] ]
[ METHOD <MethodName>( [<params,...>] ) INLINE <Code,...> ]
[ METHOD <MethodName>( [<params,...>] ) BLOCK  <CodeBlock> ]
[ METHOD <MethodName>( [<params,...>] ) EXTERN <funcName>([<args,...>]) ]
[ METHOD <MethodName>( [<params,...>] ) SETGET ]
[ METHOD <MethodName>( [<params,...>] ) VIRTUAL ]
[ METHOD <MethodName>( [<params,...>] ) OPERATOR <op> ]
[ ERROR HANDLER <MethodName>( [<params,...>] ) ]
[ ON ERROR <MethodName>( [<params,...>] ) ]
...
[PROTECTED:]
...
[VISIBLE:]
[EXPORTED:]
...

[FRIEND CLASS <ClassName,...>]
[FRIEND FUNCTION <FuncName,...>]

[SYNC METHOD <cSyncMethod>]

ENDCLASS [ LOCK | LOCKED ]


### Another example

Copied verbatim from w:Harbour (software)

 #include "hbclass.ch"

PROCEDURE Main()

LOCAL oPerson

CLS

oPerson := Person():New( "Dave" )

oPerson:Eyes := "Invalid"

oPerson:Eyes := "Blue"

Alert( oPerson:Describe() )

RETURN

CREATE CLASS Person

VAR Name INIT ""

METHOD New( cName )
METHOD Describe()

ACCESS Eyes INLINE ::pvtEyes
ASSIGN Eyes( x ) INLINE iif( HB_ISSTRING( x ) .AND. x \$ "Blue,Brown,Green", ::pvtEyes := x, Alert( "Invalid value" ) )

PROTECTED:

VAR pvtEyes

ENDCLASS

// Sample of normal Method definition
METHOD New( cName ) CLASS Person

::Name := cName

RETURN Self

METHOD Describe() CLASS Person

LOCAL cDescription

IF Empty( ::Name )
cDescription := "I have no name yet."
ELSE
cDescription := "My name is: " + ::Name + ";"
ENDIF

IF ! Empty( ::Eyes )
cDescription += "my eyes' color is: " + ::Eyes
ENDIF

RETURN cDescription


## Harbour CGI

and see the side box at http://gnosis.cx/publish/programming/harbour.html

blog live demo: https://harbour.fourtech.es/modharbour_samples/blog/index.prg and its Full source code: https://github.com/FiveTechSoft/mod_harbour/tree/master/samples/blog