GERBELOTBARILLON.COM

Parce qu'il faut toujours un commencement...

Pure Basic

Comment ? Du BASIC ? Ici ? Et bien oui car, comme disait quelqu'un dont je ne me rappelle pas le nom, ce n'est pas le langage qui est important mais la façon de l'utiliser pour résoudre un problème. Et c'est vrai que lorsque l'on voit la profusion de langages modernes pour la programmation on ne peut être que surpris de parler BASIC.

Nous aurions pu effectivement parler d'un langage utilisant le paradygme de la programmation orientée objet tel que C++ ou C# ou encore Java mais nous allons nous concentrer sur un BASIC qui n'a de basique que le nom. Bien qu'étant un langage procédural (pourvu qu'il le reste), PureBasic n'en reste pas moins un langage moderne multiplateforme, simple d'accès et très puissant. En effet, contrairement aux différents BASIC que nous avons connus, qui souffraient de beaucoup de lenteurs et de programmation "spaghetti", PureBasic est un BASIC compilé. Cela lui donne une grande vélocité dans les différents traitements, largement suffisant pour la majorité des applications que vous aurez envie de programmer.

Au contraire de nombre de langages disponibles, PureBasic est un ensemble de développement qui n'est pas gratuit. L'investissement en vaut la chandelle car la licence est acquise définitivement et vous donne accès à toutes les mises à jours disponibles, aussi bien mineures que majeures. Et puis cela permettra à un groupe de programmeurs talentueux de continuer à travailler pour nous apporter de plus en plus de fonctionnalités et de performances. Good Job Guys !

Les pré requis d'installation

Il suffit simplement de télécharger et installer l'exécutable depuis le site de l'éditeur https://www.purebasic.com/french/. Le programme est disponible en version 32 et 64 bits en fonction de l'OS que vous utilisez. Vous obtenez alors un Environnement de Développement Intégré (IDE) qui vous permettra de produire du code et de générer vos interfaces graphiques (GUI). C'est rapide à installer et l'éditeur fourni est suffisant pour vos réalisations, incorporant de la coloration syntaxique et une complétion de code.

L'IDE de PureBasic incorpore un système d'aide efficace avec un nombre d'exemples importants. Pour les appels aux différentes API, vous pouvez rajouter de l'aide externe. Sous Windows, vous pouvez ajouter les fichiers HLP relatifs aux APIs de base, ainsi que celles couvrant OpenGL, MAPI, les sockets et autres en créant un dossier Help directement dans le dossier de PureBasic. De même pour l'assembleur en ajoutant le fichier ASM.HLP, toujours dans le même dossier Help du dossier de PureBasic. Par la suite, en appuyant sur la touche F1 lorsque le curseur d'édition est sur un nom de procédure, vous devriez obtenir une fenêtre avec l'aide en ligne. Si jamais la touche F1 n'est pas opérationnelle, consultez l'aide depuis le menu Help / External Help.

Remarque : le format des fichiers d'aide est très ancien et non pris en charge nativement sous les versions de Windows postérieures à Windows XP. Pour les obtenir, il faut aller les télécharger directement depuis le site Microsoft. Attention à bien choisir la version correspondant à votre système d'exploitation. Il existe un fichier différent pour chaque version de Windows. Seul bémol : Microsoft n'a pas encore réalisé la version du programme WinHlp32.exe pour Windows 10 ;-(. Pour pallier ce défaut, vous trouverez le lien dans la colonne de droite pour télécharger les versions CHM.

BASIC procédural

Depuis la création du premier langage BASIC, nombre d'évolutions ont été apportées à ce langage. PureBasic apporte également ses propres améloirations pour en faire :

Modes de fonctionnement

PureBasic permet de réaliser des programmes pour la console (DOS, Shell, Terminal...) ou bien en mode graphique en incorporant une interface graphique (GUI). La partie graphique sera traitée ultérieurement alors intéressons-nous à la version console.

Afin de réaliser un exemple rapide, voici un simple Hello World comme nous avons l'habitude d'en voir fleurir avec tous les langages.


OpenConsole()
PrintN("Hello World of PureBasic...")
Repeat
Until Inkey() <> ""
CloseConsole()

Le code ci-dessus demande à PureBasic d'ouvrir une fenêtre en mode console et d'afficher simplement une chaîne de caractères. Le programme attend ensuite qu'une touche soit pressée pour se terminer. La console est alors fermée. Par défaut, le console ne permet d'afficher que des caractères textuels. La commande EnableGraphicalConsole() permet de basculer en mode graphique et ainsi de pouvoir utiliser le positionnement de caractères dans la console et de la couleur pour les caractères.


If OpenConsole()
    EnableGraphicalConsole(1)
    ConsoleLocate(7, 8)
    PrintN("Appuyez sur [Entree] pour quitter")
    Input()
EndIf
	

Variables et expressions

En PureBasic il n'est nul besoin de déclarer les variables que nous allons utiliser, hormis pour les structures et les tableaux. Toutefois, celles-ci peuvent contenir des types de valeurs différentes et PureBasic impose un certain typage. Le tableau ci-après donne les différents types disponibles.

Extension Description
.qUn nombre signé exprimé sur 64 bits (quad).
.iUn nombre signé entier exprimé en 32 ou 64 bits selon la plateforme (int). Type à privilégier pour la majorité des variables, sauf octet et flottant.
.lUn nombre entier long signé exprimé sur 32 bits (long).
.wUn mot signé exprimé sur 16 bits (word), soit des valeurs de -32768 à 32767.
.uUn nombre non signé de 16 bits (word), soit des valeurs de 0 à 65535.
.bUn nombre signé exprimé sur 8 bits (byte), soit des valeurs de -128 à 127.
.aUn nombre non signé exprimé sur 8 bits (byte), soit des valeurs de 0 à 255. Type à privilégier pour les accès mémoire car la mémoire fonctionne avec des octets.
.sUne chaîne de caractères
var$Une chaîne de caractères définie sous un autre format.
var${x}Une chaîne de caractères de longueur fixe.
.cUn seul caractère, entre '', exprimé sur un ou deux octets selon l'usage ou non de l'Unicode.
*p = @vDéclaration d'un pointeur sur une zone mémoire. Le pointeur peut être soit en 32 ou 64 bits selon le type de la variable et la plateforme.
.fUn nombre en virgule flottante
.dUn nombre double en virgule flottante. Type à privilégier pour les valeurs numériques à virgule.
var.structureDéclaration d'une variable de type Structure
Une fois qu'une variable est définie, il n'est plus nécessaire de redéfinir son type. Par contre, à l'opposé de certains BASIC qui permettent de réutiliser les mêmes noms de variables pour contenir différents types de données, PureBasic l'interdit. Les variables signées et non signées occupent le même espace en mémoire. C'est la façon de les interpréter qui diffère.

OpenConsole() ; ouverture d'une console pour afficher des informations

b.b = 247 ; déclaration d'un entier sur 8 bits signé
PrintN(Str(b))  ; retourne un octet signé soit -9
PrintN(StrU(b, #PB_Byte)) ; Affiche une valeur non signée sur un octet soit 247

a.a = 247 ; déclaration d'un octet sur 8 bits non signé
PrintN(Str(a))  ; affiche 247
PrintN(StrU(a)) ; affiche 247

; Autre façon d'accéder à la variable
x.a = 247
PrintN(Str(PeekB(@x))) ; affiche un octet signé soit -9
PrintN(Str(PeekA(@x))) ; affiche un octet non signé soit 247

Repeat
  Delay(1) ; laisse du temps aux autres processus systèmes
Until Inkey() <> "" ; attend la frappe d'une touche quelconque

CloseConsole() ; Fermeture de la console
	
Un octet occupe 8 bits en mémoire. En représentation binaire 247 = %11110111. Le signe est déterminé par la valeur du bit de poids fort (le plus à gauche) soit ici 1. En représentation décimale nous avons alors %(1)1110111 => complément à 2 => %(1)0001001 = -9. Pour plus d'informations sur le binaire, voir la page sur les systèmes numériques www.gerbelotbarillon.com/sysnum.html.

Pour éviter les erreurs de compilation ou les malentendus au sujet de noms de variables, PureBasic met à notre disposition la commande EnableExplicit qui va signaler au compilateur de PureBasic de ne pas accepter de variable si elle n'a pas été définie précédemment. Ainsi le code suivant va fonctionner mais ne va pas donner le résultat attendu


a = 1
a = aa + 1
Debug a  ; va donner 1 au lieu de 2 qui est la valeur attendue
	
Dans le code ci-dessus, la valeur attendue est 2 mais le compilateur va renvoyer simplement 1 car pour lui la variable aa n'est pas une erreur et va la considérer comme égale à 0 lors de son initialisation. Pour éviter ce genre de bévue, on va régler le problème en utilisant EnableExplicit et en déclarant les variables avant leur utilisation.

EnableExplicit
Define a.i ; EnableExplicit nous force à déclarer les variables avant usage
a = 1
a = aa + 1 ; Cette ligne génère une erreur à la compilation car aa n'est pas déclarée avant son utilisation
	
Si on récapitule :

Evaluation des expressions

Les expressions sont normalement évaluées de gauche à droite, à savoir que la partie à gauche du symbole '=' récupère la valeur qui est à droite de ce même symbole. Cette valeur à droite peut être un nombre direct, une variable ou bien une opération complexe faisant intervenir une combinaison de variables.

Les expressions doivent rester simples dans leur expression. Il ne faut pas confondre PureBasic et le C tout de même. On ne peut donc pas avoir quelquechose du genre b = 1 : a = (a = b) > c...

Les chaînes de caractères

Les chaînes de caractères sont de longueur variables par défaut. La fin de ces chaînes est marquée par un caractère 0. Un caractère occupe 1 octet (2 en mode Unicode).

ATTENTION : pour ceux qui proviennent d'un langage bas niveau type C, l'utilisation de chaînes de caractères en tant que buffer lors de la lecture de contenu binaire est déconseillée car un caractère zéro est si vite arrivé...et le buffer si vite corrompu... Il est alors recommandé d'utiliser plutôt un buffer de type chaîne de caractères de taille fixe. Pour ceux qui ont déjà oublié, ce type de variable se note ${x} ou .s{x}.


a.s = "Hello world"      ; taille variable, implicitement terminée par un caractère NULL
b.s{11} = "Hello world"  ; taille fixe, caractère NULL autorisé

Les chaînes de taille fixe sont très utilisées dans les structures de données ainsi que pour le passage de paramètre aux différentes API gérées par les bibliothèques externes à PureBasic.

Il est possible de convertir des nombres en chaînes de caractères et l'inverse en utilisant les couples de fonctions Val() et Str(). Val() convertit une chaîne de caractères représentant un nombre en ce nombre alors que Str() convertit un nombre en sa représentation sous forme de chaîne de caractères. Ces fonctions de conversion acceptent des déclinaisons sur des types de variables définis comme StrD() pour les conversions de nombres entiers double longueur, StrF() pour la conversion de nombres en virgule flottante ou StrU() pour les nombres non signés. Idem pour Val() avec ValF() pour convertir une chaîne de caractères représentant un nombre en virgule flottante en nombre à virgule flottante ou ValD() pour l'équivalent en nombre entier format double longueur. Il est bien sûr possible de traiter des valeurs exprimées en binaire (avec % devant) ou hexadécimal (avec un $ devant).

Opérations sur les chaînes

Nous pouvons utiliser un certain nombre de fonctions pour manipuler les chaînes de caractères afin d'extraire certains caractères, ou bien an concaténant les chaînes les unes avec les autres, pour créer des chaînes vides...


s1.s = "12"
s2.s = "345678"
s.s = s1 + s2 ; concaténation des deux chaînes de caractères
Debug s       ; Affiche "12345678"
Debug Len(s) ; 8
Debug Left(s, 4) ; affiche les 4 premiers caractères de la chaîne soit "1234"
Debug Right(s, 4) ; affiche les 4 derniers caractères de la chaîne soit "5678"
Debug Mid(s, 4, 2) ; affiche 2 caractères à partir de la position 4 de la chaîne, soit "45"
Debug Space(10) ; chaîne "          " 10 caractères espace
Debug Trim("  AZERTY         ") ; affiche "AZERTY"
Debug UCase("Hello") ; affiche "HELLO"
Debug ReplaceString(h, "45", "66") ; affiche "366678"
Debug FindString(s2, "45", 1) ; affiche la position en commençant à 1 à laquelle la chaîne est trouvée

; Le code suivant va afficher chaque mot de la chaîne séparé par le séparateur " "
For k=1 To 7
  Debug StringField("Je suis une chaîne contenant des champs", k, " ")
Next
	

Pour produire un affichage propre des informations, nous pouvons formatter la sortie de ces chaînes par les commandes LSet() et RSet().


Debug RSet("L", 4)      ; affiche "   L" soit une chaîne de 4 caractères avec des espaces pour compléter
Debug RSet("L", 4, "*") ; affiche "***L"
Debug LSet("L", 4)      ; affiche "L   "
Debug LSet("L", 4, "*") ; affiche "L***"
	

Les booléens

Les variables booléennes permettent de tester si les informations sont vraies ou fausses. Les valeurs sont #True pour Vrai et #False pour Faux. Ce sont des ajouts des versions 5 et ultérieures que vous pouvez mettre à la place des simples tests =0 pour Faux et <>0 pour Vrai. Il est donc possible aujourd'hui de faire


a = Bool(b <> c)
	
au lieu de ceci

a = #False
If b <> c
	a = #True
Endif
	

Les contrôles de flux

If / Endif / Else / Endif

Si la condition est vraie alors on exécute un morceau du programme, sinon on exécute un autre morceau de programme.


a.l = 10
If a = 10
	Debug "a vaut 10"
ElseIf a = 20
	Debug "a vaut 20"
Else
	Debug "a vaut une autre valeur"
Endif
	

Select / Case / EndSelect

L'utilisation de Select permet de tester une variable contre un grand ensemble de valeurs possibles. Cela permet d'éviter d'utiliser une multitude de If / ElseIf et de simplifier la lecture du programme. Contrairement à d'autres langages pour lesquels la construction select / Case existe (un bon vieux switch des familles), en PureBasic il est possible de tester des valeurs numériques, des chaînes de caractères et même des intervalles de données pour une variable.


a = 5
Select a
  Case 1
    Debug "un"
  Case 2,3
    Debug "deux ou trois"
  Case 4 To 10
    Debug "dans l'intervalle 4 à 10"
  Default
    Debug "valeur inconnue"
EndSelect
	
Il n'est par contre pas possible de mixer dans un Select / Case des comparaisons sur une chaîne de caractères en même temps que des valeurs numériques. Eh oui ! On compare bien sûr selon le type de la variable testée... Et pour les valeurs flottantes ? Et bien PureBasic va d'abord arrondir à la valeur inférieure avant de procéder à la comparaison.

Boucles

Pour répéter des opérations il existe 3 types de boucles :


; Gestion des boucles

; Avec For / Next
For n = 1 To 10
  Debug n
Next

; avec Repeat / Until
n = 1
Repeat
  Debug n
  n + 1
Until n > 10

; avec While / Wend
n = 1
While n <= 10
  Debug n
  n + 1
Wend
	
Avec un peu de structure dans le code, on s'aperçoit vite que la seule vraie gestion de boucles est réalisée avec un While / Wend bien calibré.

L'exception dans la gestion des boucles est la commande ForEach qui sera quasiment exclusivement utilisée pour la gestion des listes chaînées. Nous verrons dans un chapitre ultérieur de quelle manière.

Parfois il est nécessaire de pouvoir interrompre une boucle, même si normalement, si le code est bien conçu, vous ne devriez pas avoir à le faire. Il existe cependant deux instructions :

Une des seules raisons pour lesquelles vous pourriez avoir besoin de Break serait pour quitter une boucle infinie du genre Repeat / Forever.

Les structures de données

Les structures de données sont les meilleurs moyens de gérer et manipuler des ensembles d'informations. Ces structures peuvent contenir tous types de variables.


; Structures
; Tous les exemples sont donnés pour une configuration en mode non-Unicode (soit un caractère sur un octet)

; Avec une chaîne de caractères
Structure tree
  a.l
  b.s
EndStructure

apple.tree ; variable de type 'tree' (une structure de données)
apple\a = 1
apple\b = "Apple"

Debug(apple\a)
Debug(apple\b)


; Avec un tableau d'octets
Structure tree_octets
  a.l
  b.b[16]
EndStructure

apple2.tree_octets
apple2\a = 2
PokeS(@apple2\b, "Apple 2") ; écrit 8 caractères dans la zone mémoire (7 octets + 1 caractère de fin de chaîne)

Debug(apple2\a)
Debug(apple2\b) ; n'affiche que le premier caractère du tableau
Debug(PeekS(@apple2\b)) ; affiche toute la chaîne à l'adresse apple2\b

Debug("")
For n = 0 To 7
  Debug(Hex(PeekA(@apple2\b+n))) ; affichage du code Ascii du caractère en RAM
Next


; Avec une chaîne de longueur fixe
Structure tree_fixed
  a.l
  b.s{10}
EndStructure

apple3.tree_fixed
apple3\a = 3
apple3\b = "Apple3"

Debug ""
Debug apple3\b

PokeA(@apple3\b+5, '4') ; Modification du 6eme caractère de la chaîne
Debug apple3\b

apple3\b = "PureBasic is my name" ; la chaîne dépasse le buffer mais ne génère pas d'erreur
Debug apple3\b ; affiche les 10 caractères du buffer défini dans la structure

Les procédures et les fonctions

Historiquement les langages de programmation ont toujours fait une différence entre fonctions et procédures. Les fonctions sont des procédures qui retournent un résultat. PureBasic a simplifié en stipulant qu'une procédure pouvait également renvoyer un résultat à la fin du traitement. Une procédure est un élément clé dans un programme car cela permet d'effectuer un ensemble d'actions plusieurs fois sans dupliquer le code requis pour ces actions. Comme tout objet structuré de PureBasic, la définition d'une procédure se termine par la commande EndProcedure.


Procedure proc(a.l, b.l, c.d)
	Debug a
	c = a + b
	ProcedureReturn c
EndProcedure
	
Le morceau de code ci-dessus montre la déclaration d'une procédure avec 3 paramètres en entrée. La valeur de retour est transmise par la commande ProcedureReturn. Cette commande peut être placée n'importe où dans le corps de la procédure. Elle est soit :

Les procédures peuvent prendre des paramètres en entrée, en nombre fixe ou bien avec des paramètres optionnels disposant de valeurs par défaut. Ainsi les appels à la procédure peuvent prendre un nombre variable de paramètres :


Procedure procvar(a.l, b.d, c.l=42)
	Debug a
	Debug b
	Debug c
EndProcedure
; ...
procvar(1, 2.0)     ; appel avec deux paramètres
procvar(3, 4.0, 33) ; appel avec trois paramètres
	

Portée des variables

On désigne par portée d'une variable l'emplacement du programme dans lequel on peut utiliser une variable à partir de l'endroit où celle-ci a été déclarée. On distingue la portée locale et la portée globale.


Global c.l ; variable déclarée globale pour tout le programme

Procedure proc()
  a.l = 42
  b.l = 33
  c.l = 25
EndProcedure

; Début du programme principal
a.l = 1
b.l = 2
c.l = 3

proc()

Debug a  ; affiche 1
Debug b  ; affiche 2
Debug c  ; affiche 25
	
On déclare une variable 'c' comme globale à tout le programme. Suite à la définition des variables 'a' et 'b' (considérées comme 'locales' au programme) et à l'affectation d'une valeur à chacune d'elles, la variable 'c' précédemment déclarée comme globale se voit également affectée d'une valeur. Ensuite nous faisons un appel à la procédure proc() qui va modifier les valeurs de a, b et c. Nous affichons ensuite les valeurs résultantes pour s'apercevoir que :

Il se pourrait que vous ayez besoin de déclarer une variable locale à une procédure de même nom qu'une variable globale. Cela peut arriver oui oui. Pour cela, il va falloir utiliser la commande Protected.


Global a
a = 10
  
Procedure Change()
	Protected a 
   a = 20
EndProcedure 
  
Debug a ; Affichera 10 car la variable a été protégée.
	

La dernière façon de déclarer une variable est d'utiliser le mot-clé Static. Une variable déclarée comme statique a priorité sur les variables globales et n'est pas détruite entre chaque appel à la procédure. Une variable globale qui porterait le même nom qu'une variable de procédure déclarée comme statique ne serait pas prise en compte.


Global a
a = 10
  
Procedure Change()
	Static a
   a+1
   Debug "Dans la Procédure: "+Str(a) ; Affichera 1, 2, 3 car la variable s'incrémente à chaque appel de la procédure.
EndProcedure 

Change()
Change()
Change()
Debug a ; Affichera 10, car une variable 'static' n'affecte pas une variable 'global'.
	

En fait il existe d'autres types de portées pour les variables :


Liens intéressants

http://www.purebasic.com/french/index.php
Page principale du logiciel PureBasic, avec accès au téléchargement, aux documentations et au forum des utilisateurs qui est très bien fourni en codes de toutes sortes.
https://support.microsoft.com/kb/917607
Site Web Microsoft pour récupérer la version du lecteur de fichiers d'aide au format HLP relative à votre version d'OS.
Win32hlp au format CHM
Comme le format HLP n'est pas disponible pour l'instant pour Windows 10, vous pouvez télécharger les fichiers mis au format CHM. Il faudra les placer dans le dossier Help de PureBasic pour y accéder par l'aide externe.

Compléments

http://www.purebasic.fr/french/
Le forum officiel en Français de PureBasic. C'est par ici qu'il faut commencer vos recherches.
http://www.pureare.net
Un des sites de référence pour PureBasic.
http://www.purebasic.fr/blog/
Le blog de la team PureBasic. Beaucoup d'informations depuis 2008
http://bluez.home.xs4all.nl/purebasic/index.htm#top
Une bonne documentation sur l'ensemble des éléments de PureBasic. Dommage que la version 5 ne soit pas complètement prise en compte dans les explications...
http://purebasic.developpez.com/sources/
L'incontournable site pour une énorme majorité de liens sur le développement pour tous les langages et les systèmes existants.
http://www.reelmedia.org/pureproject
Une source intéressante de réalisations et de liens vers les informations de PureBasic.
http://www.purebasicstreet.com
Dispose de codes et ressources prêts à l'emploi.
http://www.pbfrance.com
Des codes sources dédiés uniquement à PureBasic.

Cet article est inspiré du site de Bluez dont les coordonnées sont définies dans la colonne d'accès rapide du site. J'ai essayé de simplifier et d'apporter des modifications intéressantes sans vous noyer sous les définitions. Je ne suis pas sûr d'y être parvenu...