Port (język programowania) - Harbour (programming language)

Projekt Portowy
Paradygmat wieloparadygmat : imperatyw , funkcjonalny , obiektowy , refleksyjny
Zaprojektowany przez Antonio Linares
Deweloper Wiktor Szakáts i społeczność
Po raz pierwszy pojawiły się 1999 ; 22 lata temu ( 1999 )
Wersja stabilna
3.0.0 / 17 lipca 2011 ; 10 lat temu ( 17.07.2011 )
Wersja zapoznawcza
Dyscyplina pisania Opcjonalnie kaczka , dynamiczna , bezpieczna , częściowo mocna
OS Wieloplatformowy
Licencja Zgodny z Open Source GPL
Rozszerzenia nazw plików .prg, .ch, .hb, .hbp
Strona internetowa port .github .io
Dialekty
Clipper, Xbase++, FlagShip, FoxPro, xHarbour
Wpływem
dBase, Clipper
Pod wpływem
xHarbour

Harbour to język programowania komputerowego , używany głównie do tworzenia programów bazodanowych/biznesowych. Jest to zmodernizowana, open source i wieloplatformowa wersja starszego systemu Clipper , który z kolei rozwinął się z rynku baz danych dBase z lat 80. i 90. XX wieku.

Kod Harbour przy użyciu tych samych baz danych może być kompilowany na wielu różnych platformach, w tym Microsoft Windows , Linux , warianty Unix , kilku potomków BSD , Mac OS X , MINIX 3 , Windows CE , Pocket PC , Symbian , iOS , Android , QNX , VxWorks , OS/2 (w tym eComStation i ArcaOS ), BeOS / Haiku , AIX i MS-DOS .

Historia

Idea darmowego kompilatora Clippera pojawiała się od dawna i temat ten często pojawiał się w dyskusji na comp.lang.clipper. Antonio Linares założył projekt Port, a jego realizację rozpoczęto w marcu 1999 roku. Nazwa "Port" została zaproponowana przez Linares, jest to zabawa na Clipperze jako typie statku. Port jest synonimem portu (gdzie cumują statki), a port to port języka Clipper.

W 2009 roku port został gruntownie przebudowany, głównie przez Viktora Szakátsa i Przemysława Czerpaka.

Obsługa baz danych

Harbour rozszerza podejście Clipper Replaceable Database Drivers (RDD). Oferuje wiele RDD, takich jak DBF , DBFNTX, DBFCDX, DBFDBT i DBFFPT. W Harbour wiele RDD może być używanych w jednej aplikacji, a nowe logiczne RDD mogą być definiowane przez połączenie innych RDD. Architektura RDD umożliwia dziedziczenie, dzięki czemu dany RDD może rozszerzać funkcjonalność innych istniejących RDD. RDD innych firm, takie jak RDDSQL, RDDSIX, RMDBFCDX, Advantage Database Server i Mediator, stanowią przykład niektórych funkcji architektury RDD. Implementacja DBFNTX ma prawie taką samą funkcjonalność jak DBFCDX i RDDSIX. NETIO i LetoDB zapewniają zdalny dostęp przez protokół TCP .

Harbour oferuje również obsługę ODBC za pomocą składni OOP oraz obsługę ADO za pomocą OLE . MySQL , PostgreSQL , SQLite , Firebird , Oracle to przykłady baz danych, z którymi Harbour może się połączyć.

Technologie xBase są często mylone z oprogramowaniem RDBMS . Chociaż to prawda, xBase jest czymś więcej niż prostym systemem bazodanowym, ponieważ jednocześnie języki xBase używające czysto DBF nie mogą zapewnić pełnej koncepcji prawdziwego RDBMS.

Filozofia programowania

W przeciwieństwie do Javy, która jest przeznaczona do jednorazowego napisania i uruchomienia w dowolnym miejscu, Harbour ma na celu jednokrotne napisanie i kompilację w dowolnym miejscu . Ponieważ ten sam kompilator jest dostępny dla wszystkich powyższych systemów operacyjnych, nie ma potrzeby przekodowywania w celu utworzenia identycznych produktów dla różnych platform, z wyjątkiem sytuacji, gdy używane są funkcje zależne od systemu operacyjnego. Kompilacja skrośna jest obsługiwana przez MinGW . W systemie Microsoft Windows Harbour jest bardziej stabilny, ale gorzej udokumentowany niż Clipper, ale obsługuje wiele platform, jest bardziej przejrzysty, konfigurowalny i może działać z dysku flash USB.

Pod Linuksem i Windows Mobile kod źródłowy Clippera można skompilować za pomocą Harbour z bardzo małą adaptacją. Większość oprogramowania oryginalnie napisanego pod Xbase++, FlagShip, FoxPro, xHarbour i innych dialektach może być skompilowana z Harbour z pewną adaptacją. W 2010 r. podjęto wiele wysiłków, aby ułatwić przejście z innych dialektów xBase .

Harbour może korzystać z następujących kompilatorów C m.in.: GCC , MinGW , Clang , ICC , Microsoft Visual C++ (6.0+), Borland C++ , Watcom C , Pelles C i Sun Studio .

Harbour może korzystać z wielu emulacji terminala graficznego , w tym sterowników konsoli i konsoli hybrydowych/GUI, takich jak GTWvt i GTWvg.

Harbour obsługuje zewnętrzne GUI, darmowe (np. HBQt, HWGui, MiniGUI (najnowsza wersja oparta na Qt i QtContribs ) oraz komercyjne (np. FiveWin, Xailer). HBQt to biblioteka dostarczająca powiązania do aplikacji Qt. HBIDE to próbka potencjału HBQt.

Harbour jest w 100% kompatybilny z Clipperem i obsługuje wiele rozszerzeń składni języka, w tym znacznie rozszerzone biblioteki wykonawcze , takie jak OLE , Blat , OpenSSL , FreeImage , GD , hbtip, hbtpathy, PCRE , hbmzip ( zlib ), hbbz2 ( bzip2 ), cURL , Cairo , własna implementacja CA-Tools, zaktualizowane biblioteki NanFor i wiele innych. Harbour ma aktywną społeczność programistów i szerokie wsparcie stron trzecich.

Każdy język xBase zapewnia bardzo produktywny sposób tworzenia aplikacji biznesowych i aplikacji intensywnie korzystających z danych. Port nie jest wyjątkiem.

Operator makr (kompilator środowiska uruchomieniowego)

Jedną z najpotężniejszych funkcji języków xBase jest operator makr '&'. Implementacja operatora makr w Harbour umożliwia kompilację w czasie wykonywania dowolnego poprawnego wyrażenia Harbour. Tak skompilowane wyrażenie może być użyte jako WARTOŚĆ, czyli prawa strona przypisania (r-wartość), ale tak skompilowane wyrażenie może być użyte do rozwiązania lewej strony (l-wartości) przypisania, czyli zmiennych prywatnych lub publicznych, lub pole bazy danych.

Dodatkowo operator makr może kompilować i wykonywać wywołania funkcji, kompletne przypisania, a nawet listę argumentów, a wynik makra może być użyty do rozwiązania dowolnego z powyższych kontekstów w skompilowanej aplikacji. Innymi słowy, każda aplikacja Harbour może zostać rozszerzona i zmodyfikowana w czasie wykonywania w celu skompilowania i wykonania dodatkowego kodu na żądanie.

Najnowszy kompilator makr może skompilować dowolny poprawny kod Harbour, w tym kod do wstępnego przetworzenia przed kompilacją.

Składnia:

 &( ... )

Wartość tekstowa wyrażenia '...' zostanie skompilowana, a wynikiem będzie wartość wynikająca z wykonania skompilowanego kodu.

 &SomeId

to krótka forma dla &( SomeId ).

 &SomeId.postfix

jest krótką formą &( SomeId + "postfix" ).

Programowanie obiektowe

Programowanie w stylu OOP jest szerszym zagadnieniem niż konkretna biblioteka lub określony interfejs, ale programowanie OOP jest czymś, czego wielu programistów Clippera oczekuje. CA-Clipper 5.2, a zwłaszcza 5.3, dodały kilka klas bazowych i pasującą składnię OOP. Biblioteki, takie jak Class(y) , Fivewin, Clip4Win i TopClass, zapewniają dodatkową funkcjonalność OOP.

Harbour posiada rozszerzenia OOP z pełną obsługą klas, w tym dziedziczenia, w oparciu o składnię Class(y). Składnia OOP w Harbour jest bardzo podobna do tej z wcześniejszych bibliotek klas Clippera, więc powinno być możliwe utrzymanie starszego kodu Clippera przy minimalnych zmianach.

Składnia i semantyka

Kod portu na HBIDE

Harbour, ponieważ każdy język xBase nie rozróżnia wielkości liter i opcjonalnie może akceptować słowa kluczowe napisane tylko przez ich pierwsze cztery znaki.

Wbudowane typy danych

Harbour ma sześć typów skalarnych: Nil , String , Date , Logical , Numeric , Pointer , oraz cztery typy złożone: Array , Object , CodeBlock i Hash . Skalar zawiera pojedynczą wartość, taką jak łańcuch, numer lub odwołanie do dowolnego innego typu. Tablice są uporządkowanymi listami skalarów lub typów złożonych, indeksowanych według liczby, zaczynając od 1. Hasze lub tablice asocjacyjne to nieuporządkowane kolekcje wartości dowolnego typu, indeksowane przez skojarzony z nimi klucz, który może być dowolnego typu skalarnego lub złożonego.

Dosłowna (statyczna) reprezentacja typów skalarnych:

  • Zero: NIL
  • Strunowy: "hello", 'hello', [hello]
  • Data: 0d20100405
  • Logiczny: .T., .F.
  • Numeryczne: 1, 1.1, −1, 0xFF

Typy złożone mogą być również reprezentowane jako wartości dosłowne:

  • Szyk: { "String", 1, { "Nested Array" }, .T., FunctionCall(), @FunctionPointer() }
  • Blok kodu :{ |Arg1, ArgN| Arg1 := ArgN + OuterVar + FunctionCall() }
  • Haszysz: { "Name" => "John", 1 => "Numeric key", "Name2" => { "Nested" => "Hash" } }

Hasze mogą używać dowolnego typu, w tym innych skrótów jako klucza dla dowolnego elementu. Skróty i tablice mogą zawierać dowolny typ jako wartość dowolnego elementu członkowskiego, w tym tablice zagnieżdżone i skróty.

Bloki kodu mogą mieć odniesienia do Zmiennych metody Procedura/Funkcja>, w której zostały zdefiniowane. Takie bloki kodu mogą być zwracane jako wartość lub za pomocą argumentu przekazanego przez REFERENCE , w takim przypadku blok kodu "przeżyje" procedurę, w której został zdefiniowany, a wszelkie zmienne, do których się odwołuje, będą zmienną DETACHED .

Odłączone zmienne będą zachowywać swoją wartość tak długo, jak długo istnieje odwołujący się do nich blok kodu. Takie wartości będą współdzielone z każdym innym Codeblockiem, który może mieć dostęp do tych samych zmiennych. Jeśli blok kodu nie przeżył swojej procedury zawierającej i będzie oceniany w okresie życia procedury, w której jest zdefiniowany, zmiany w jego zmiennych odłączanych za pomocą jego oceny zostaną odzwierciedlone z powrotem w jego procedurze rodzicielskiej.

Bloki kodu mogą być oceniane dowolną liczbę razy za pomocą funkcji Eval( BlockExp ) .

Zmienne

Wszystkie typy można przypisać do nazwanych zmiennych. Nazwane identyfikatory zmiennych mają długość od 1 do 63 znaków ASCII, zaczynają się od [A-Z|_]i dalej składają się ze znaków [A-Z|0–9|_]do maksymalnie 63 znaków. W nazwanych zmiennych nie jest rozróżniana wielkość liter.

Zmienne mają jeden z następujących zakresów:

  • LOCAL : Widoczne tylko w procedurze, która to zadeklarowała. Wartość jest tracona po wyjściu z rutyny.
  • STATIC : Widoczne tylko w procedurze, która to zadeklarowała. Wartość jest zachowywana dla kolejnych wywołań procedury. Jeśli zmienna STATIC jest zadeklarowana przed zdefiniowaniem jakiejkolwiek procedury/funkcji/metody, ma ona zakres MODULE i jest widoczna w dowolnej procedurze zdefiniowanej w tym samym pliku źródłowym, zachowa swoją żywotność przez czas życia aplikacji.
  • PRYWATNE : Widoczne w procedurze, która ją zadeklarowała, i wszystkich procedurach wywołanych przez tę procedurę.
  • PUBLIC : Widoczny przez wszystkie procedury w tej samej aplikacji.

LOCAL i STATIC są rozwiązywane w czasie kompilacji, a zatem są znacznie szybsze niż zmienne PRYWATNE i PUBLICZNE , które są jednostkami dynamicznymi, do których dostęp uzyskuje się za pomocą tabeli symboli w czasie wykonywania . Z tego samego powodu zmienne LOCAL i STATIC nie są udostępniane kompilatorowi makr, a każdy kod makra, który próbuje się do nich odwoływać, wygeneruje błąd w czasie wykonywania.

Ze względu na dynamiczny charakter zmiennych PRYWATNYCH i PUBLICZNYCH , można je tworzyć i niszczyć w czasie wykonywania, można do nich uzyskiwać dostęp i modyfikować je za pomocą makr środowiska uruchomieniowego, a także można do nich uzyskiwać dostęp i modyfikować je za pomocą bloków kodu utworzonych w locie.

Struktury kontrolne

Podstawowe struktury sterujące obejmują wszystkie standardowe struktury sterujące dBase i Clipper oraz dodatkowe inspirowane językami programowania C lub Java :

Pętle

[DO] WHILE ConditionExp
   ...
   [LOOP]
   [EXIT]
END[DO]
FOR Var := InitExp TO EndExp [STEP StepExp]
   ...
   [LOOP]
   [EXIT]
NEXT
FOR EACH Var IN CollectionExp
   ...
   [Var:__enumIndex()]
   [LOOP]
   [EXIT]
NEXT
  • ... jest sekwencją jednego lub więcej sprawozdań Harbor, a nawiasy kwadratowe []oznaczają opcjonalnie składniowe.
  • Var __ enumIndex () można ewentualnie stosować w celu pobrania indeks bieżącego iteracji (1) na podstawie.
  • Instrukcja LOOP ponownie uruchamia bieżącą iterację otaczającej struktury pętli, a jeśli otaczająca pętla jest pętlą FOR lub FOR EACH , zwiększa iterator, przechodząc do następnej iteracji pętli.
  • Instrukcja EXIT natychmiast kończy wykonywanie otaczającej struktury pętli.
  • Instrukcja NEXT zamyka strukturę sterowania i przechodzi do następnej iteracji struktury pętli.

W DLA deklaracji, przypisanie wartości wyrażenia przed pierwszej iteracji. DO ekspresję i porównano z wartością zmiennej sterowania, przed każdym powtórzeniu, a pętla zostanie zakończone, jeśli ocenia się na wartość większą niż numerycznej wartości liczbowej zmiennej sterowania. Opcjonalne wyrażenie STEP jest oceniane po każdej iteracji, przed podjęciem decyzji, czy wykonać następną iterację.

W For Each The Var zmienna będzie miał wartość (skalarną lub kompleks) z odpowiednim elementem wartości poboru. Wyrażenie kolekcji może być tablicą (dowolnego typu lub kombinacji typów), tabelą mieszania lub typem Object.

Oświadczenia IF

IF CondExp
   ...
[ELSEIF] CondExp
   ...
[ELSE]
   ...
END[IF]

... reprezentuje 0 lub więcej instrukcji .

Wyrażenie(a) warunku musi zostać ocenione jako wartość LOGICZNA .

Oświadczenia SWITCH

Harbour obsługuje konstrukcję SWITCH inspirowaną implementacją switch() w C.

SWITCH SwitchExp
CASE LiteralExp
   ...
   [EXIT]
[CASE LiteralExp]
   ...
   [EXIT]
[OTHERWISE]
   ...
END[SWITCH]
  • LiteralExp muszą być zestawiane razem ekspresji rozpoznawana numeryczna, i mogą obejmować operatorów, tak długo jak takie operatorzy obejmować wartości statycznego kompilacji.
  • Wyjście opcjonalnie stwierdzenie jest odpowiednikiem C sprawozdania przerwy , a jeśli jest obecny, wykonanie konstrukcji przełącznik kończy się po osiągnięciu stwierdzenie WYJŚCIE, w przeciwnym przypadku przejść do pierwszej instrukcji poniżej następnej instrukcji przypadku (nie mieści się).

BEGIN SEKWENCJA wypowiedzi

BEGIN SEQUENCE
   ...
   [BREAK]
   [Break( [Exp] )]
RECOVER [USING Var]
   ...
END[SEQUENCE]

lub:

BEGIN SEQUENCE
   ...
   [BREAK]
   [Break()]
END[SEQUENCE]

Struktura BEGIN SEQUENCE pozwala na dobrze zachowane przerwanie dowolnej sekwencji, nawet podczas krzyżowania zagnieżdżonych procedur/funkcji. Oznacza to, że wywołana procedura/funkcja może wydać instrukcję BREAK lub wyrażenie Break(), aby wymusić rozwinięcie dowolnej zagnieżdżonej procedury/funkcji, aż do pierwszej zewnętrznej struktury BEGIN SEQUENCE, po odpowiedniej instrukcji END lub klauzula RECOVER, jeśli jest obecna. Instrukcja Break może opcjonalnie przekazywać dowolny typ wyrażenia, który może być zaakceptowany przez instrukcję RECOVER, aby umożliwić dalszą obsługę odzyskiwania.

Ponadto obiekt Harbor Error obsługuje właściwości canDefault , canRetry i canSubstitute , które umożliwiają programom obsługi błędów wykonanie pewnych przygotowań, a następnie zażądanie operacji Retry , Resume lub zwrócenie Value w celu zastąpienia wyrażenia wyzwalającego warunek błędu.

Alternatywnie instrukcje TRY [CATCH] [FINALLY] są dostępne w bibliotece xhb działającej jak konstrukcja SEQUENCE.

Procedury/funkcje

[STATIC] PROCEDURE SomeProcedureName
[STATIC] PROCEDURE SomeProcedureName()
[STATIC] PROCEDURE SomeProcedureName( Param1 [, ParamsN] )
INIT PROCEDURE SomeProcedureName
EXIT PROCEDURE SomeProcedureName
[STATIC] FUNCTION SomeProcedureName
[STATIC] FUNCTION SomeProcedureName()
[STATIC] FUNCTION SomeProcedureName( Param1 [, ParamsN] )

Procedury / funkcje w Harbour można określić za pomocą słów kluczowych PROCEDURE lub FUNCTION. Reguły nazewnictwa są takie same jak dla zmiennych (do 63 znaków bez rozróżniania wielkości liter). Zarówno Procedury, jak i Funkcje mogą być kwalifikowane przez kwalifikator zakresu STATIC, aby ograniczyć ich użycie do zakresu modułu, w którym jest zdefiniowany.

INIT lub EXIT opcjonalne kwalifikacyjne, wola flagi procedura jest automatycznie wywoływana tuż przed wywołaniem procedury aplikacji startowej, lub tuż po wyjściu z aplikacji, odpowiednio. Parametry przekazane do procedury/funkcji pojawiają się w podprogramie jako zmienne lokalne i mogą akceptować dowolny typ, w tym odwołania.

Zmiany zmiennych argumentów nie są odzwierciedlane w odpowiednich zmiennych przekazywanych przez wywołującą procedurę/funkcję/metodę, chyba że są one jawnie przekazywane przez REFERENCE przy użyciu prefiksu @ .

PROCEDURE nie ma wartości zwracanej, a jeśli zostanie użyta w kontekście Expression, da wartość NIL .

FUNKCJA może zwrócić dowolny typ za pomocą instrukcji RETURN, w dowolnym miejscu w treści swojej definicji.

Przykładowa definicja procedury i wywołanie funkcji:

 x := Cube( 2 )

 FUNCTION Cube( n )
    RETURN n ** 3

Przykładowy kod

Typowy program " hello world " to:

  ? "Hello, world!"

Lub:

  QOut( "Hello, world!" )

Lub:

  Alert( "Hello, world!" )

Lub ujęte w wyraźnej procedurze:

 PROCEDURE Main()

    ? "Hello, world!"

    RETURN

Przykłady OOP

Główna procedura:

 #include "hbclass.ch"

 PROCEDURE Main()

    LOCAL oPerson

    CLS

    oPerson := Person():New( "Dave" )
    oPerson:Eyes := "Invalid"
    oPerson:Eyes := "Blue"

    Alert( oPerson:Describe() )

    RETURN

Definicja klasy:

 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

Narzędzia

  • hbmk2 – narzędzie do budowania, takie jak make
  • hbrun – interpreter powłoki dla portu. Kompilacja makr pozwala na uruchomienie dowolnego poprawnego kodu Harbour podczas kompilacji
  • hbformat – Formatuje kod źródłowy napisany w porcie lub innym dialekcie zgodnie z określonymi zasadami
  • hbpp – Preprocesor, potężne narzędzie, które pozwala uniknąć typowych problemów występujących w preprocesorze języka C
  • hbi18n – Narzędzia do lokalizacji tekstu w aplikacjach
  • hbdoc – Tworzy dokumentację dla Harbour

Wszystkie narzędzia są wieloplatformowe.

Rozwój

HBIDE wygląd.

Dziś rozwój portu jest kierowany przez Viktora Szakátsa we współpracy z Przemysławem Czerpakiem, który również wnosi wiele komponentów języka podstawowego i komponentów uzupełniających. HBIDE i niektóre inne komponenty, zwłaszcza HBQt, zostały opracowane przez Pritpal Bedi. Inni członkowie społeczności programistów wysyłają zmiany do repozytorium źródłowego GitHub . Od 2015 roku rozwój portu jest aktywny i dynamiczny.

xPorównanie portów

xHarbour to rozwidlenie wcześniejszego projektu Harbour. xHarbour stosuje bardziej agresywne podejście do implementowania nowych funkcji w języku, podczas gdy Harbor jest bardziej konserwatywny w swoim podejściu, dążąc przede wszystkim do dokładnej replikacji zachowania Clippera, a następnie implementując nowe funkcje i rozszerzenia jako drugorzędne kryterium. Należy również zauważyć, że Harbour jest obsługiwany w wielu różnych systemach operacyjnych, podczas gdy xHarbour tak naprawdę obsługuje tylko 32-bitowe systemy MS Windows i Linux.

Twórcy Harbour próbowali udokumentować wszystkie ukryte zachowania w języku Clipper i przetestować kod skompilowany przez Harbour wraz z tym samym kodem skompilowanym z Clipperem, aby zachować kompatybilność.

Twórcy Harbour wyraźnie odrzucają rozszerzenia do języka, w którym te rozszerzenia mogą złamać kompatybilność z Clipperem. Te odrzucenia zostały ostatnio złagodzone, ponieważ nowa architektura Harbour pozwala na rozszerzenia poza kompilator jądra.

Szczegółowe porównanie rozszerzeń zaimplementowanych w Harbour i xHarbour można znaleźć w repozytorium źródłowym projektu na GitHub.

Biblioteki i narzędzia GUI

  • hbide - Zintegrowane środowisko programistyczne, które pomaga w rozwoju portu i różnych dialektach xBase
  • PTSource IDE zintegrowane środowisko programistyczne obejmuje port.
  • HwGui – wieloplatformowa biblioteka GUI typu Open Source dla Harbor
  • HMG – Darmowy / Open Source xBase Win32 /system programistyczny GUI dla Harbor
  • MiniGUI – Free / Open Source xBase Win32 / GUI Development System ( Fork (rozwój oprogramowania) zarówno HMG, jak i Harbour)
  • ooHG – Object Oriented Harbour GUI – rozwidlenie „programowanie oparte na klasach i oop” HMG
  • Marinas-GUI — wieloplatformowy pakiet programistyczny GUI oparty na QT dla portu. Marinas-GUI jest pobierany jako kompletny pakiet instalacyjny dla wybranej platformy docelowej (IDE, kontrola wersji, kompilator Harbour/C, biblioteki itp.) - Zainstaluj i rozpocznij kodowanie oraz kompilację.

Zobacz też

Bibliografia

Zewnętrzne linki