Skip to main content

Harbour Basics

Data Types and Structures

Harbour has a flexible set of data types and structures. Variables are dynamic, with their type determined at runtime.

  • nil: Represents a variable with no value.
  • string: A string of characters.
  • numeric: For both integer and floating-point numbers.
  • date: Stores a calendar date.
  • datetime: Stores both a date and a time.
  • logical: A boolean value that is either .t. (true) or .f. (false).
  • array: A dynamic, multi-dimensional collection of elements of any type.
  • codeblock: A self-contained block of code stored in a variable for later execution.
  • object: An instance of a class.
  • hash: A data structure that stores key-value pairs, allowing for quick lookups (associative array).
  • pointer: A variable that holds the memory address of another variable or object.

Variable Scopes

Harbour uses different keywords to define the scope, or visibility, of a variable.

  • local: The most common scope. A local variable is only visible within the function or method where it is declared.
  • private: A variable visible within the current function and any other functions it calls.
  • public: A variable that is globally visible throughout the entire program. Use this sparingly to avoid naming conflicts.
  • static: A special type of variable that retains its value between function calls. A static variable is declared within a function but its value persists, unlike a local variable which is re-initialized on each call.

Variable type declaration

Harbour allows to declare the type of variables but it is only a source code decoration and it's ignored by the compiler. The list of supported types is: anytype, array, codeblock, class, date, datetime, hash, logical, numeric, object, pointer, symbol.

  • local var1 as string, var2 as numeric or local var1 as string := "foo", var2 as numeric := 16.

Class, Function, and Hash Examples

Harbour supports object-oriented programming (OOP) for defining classes, which act as blueprints for objects.

#include "dothrb.ch"

function main()

local oMyCar := new Car("Ford", "Fiesta", 1993)

outstd(oMyCar:getDetails())

return nil

// Class specification
class Car
var make
var model
var year

method init constructor

method getDetails
end class

// Class implementation
method init( cMake, cModel, nYear ) class Car
::make := cMake
::model := cModel
::year := nYear
return Self

method getDetails() class Car
return "Make: " + ::make + ", Model: " + ::model + ", Year: " + hb_ntos(::year)

In this example, the Car class has var (instance variables) and methods (functions) that operate on that data. The init() method is a constructor, and getDetails() is a regular method.

Harbour also uses functions, which are independent blocks of code that can be called and reused.

#include "dothrb.ch"

function main()
outstd(sumNumbers(5, 3))
return nil

function sumNumbers( nNum1, nNum2 )
local nResult := nNum1 + nNum2
return nResult

The function keyword defines a function named sumNumbers that accepts two arguments. The local keyword declares a variable that is only accessible within the function. The return statement sends a value back to the caller.

Here is an example of a hash (or associative array) in Harbour.

#include "dothrb.ch"

function main()
local oPerson := { "name" => "John Doe", "age" => 30, "city" => "New York" }

outstd(oPerson["name"], hb_eol()) // Output: John Doe
outstd(oPerson["age"], hb_eol()) // Output: 30
return nil

In this example, the curly braces {} and the => operator are used to define a hash with key-value pairs. Values are accessed by their corresponding keys inside square brackets [].


Statements

Harbour uses various statements to control program flow:

  • assignment: The := operator assigns a value to a variable.
  • conditional statements: if...elseif...else...end if and do case...case...otherwise...end case execute code blocks based on conditions. The switch...case...otherwise...end switch structure is also available as a modern alternative for multi-choice scenarios.
  • loops: for...next, for each...next, do while...end do, and while...end while are used for repetitive tasks. The while loop is an alternative to do while, providing a simple loop with a single condition at the start.
  • error handling: The begin sequence...break...end sequence construct provides a structured way to handle errors.
  • function calls: A function is executed by its name followed by its arguments in parentheses.

Dynamic Capabilities

Harbour's dynamic capabilities are a key part of its power and flexibility.

  • Macro Operator (&): This is a powerful feature that allows for runtime code compilation. The compiler at runtime evaluates the expression following the ampersand &, compiles it, and executes the resulting code. This means you can create dynamic expressions on the fly based on user input or other program variables.
  • Regex Support: Harbour includes support for regular expressions (regex) through its built-in functions. Regular expressions are a powerful tool for pattern matching and manipulation of strings.

Operators and Precedence

Harbour includes a comprehensive set of operators for performing various operations. The precedence of these operators determines the order in which they are evaluated in an expression. A higher precedence means the operator is evaluated first.

Logical Operators

  • .and.: Logical AND.
  • .or.: Logical OR.
  • !: Logical NOT.

Comparison Operators

  • == (exact equality), < (less than), > (greater than), <= (less than or equal to), >= (greater than or equal to), <> or != (not equal to).

Arithmetic Operators

  • + (addition), - (subtraction), * (multiplication), / (division), ^ or ** (exponentiation).

Bitwise Operators

These operators work directly on the individual bits of numeric values.

  • !~: Bitwise NOT.
  • &~: Bitwise AND.
  • |~: Bitwise OR.
  • ^~: Bitwise XOR.
  • <<: Left Shift.
  • >>: Right Shift.

Operator Precedence (Highest to Lowest)

  1. Prefix Operators: Unary +, -, !, ++, --, etc.
  2. Exponentiation: ^ and **.
  3. Multiplication and Division: *, /.
  4. Addition and Subtraction: +, -.
  5. Comparison: ==, <>, !=, >, <, >=, <=.
  6. Logical NOT: .not..
  7. Logical AND: .and..
  8. Logical OR: .or..
  9. Assignment: :=.

Parentheses () can always be used to override the default precedence and force a specific order of evaluation.


Preprocessor

The Harbour preprocessor processes source code before compilation, simplifying it and enabling conditional logic.

  • #define: Defines a macro or symbolic constant.
  • #include: Inserts the content of another source file.
  • #if, #ifdef, #ifndef: Directives for conditional compilation, allowing for different code to be compiled for various platforms or features.
  • #xcommand, #xtranslate: Powerful directives for creating custom syntax.