4D v16.3

Précisions de syntaxe

Accueil

 
4D v16.3
Précisions de syntaxe

Précisions de syntaxe  


 

 

Le compilateur suit la syntaxe habituelle des commandes 4D et, en ce sens, ne vous demande aucun comportement particulier dans l’optique de la compilation.

Cette section propose cependant certains rappels et quelques précisions :

  • Certaines commandes influant sur le type d’une variable peuvent, si vous n’y prêtez pas attention, conduire à des conflits de type.
  • Certaines commandes admettant des syntaxes ou des paramètres différents, il est préférable de savoir ce qu’il est plus approprié de choisir.

Pour les routines opérant sur les chaînes, seule la fonction Character code réclame une attention plus particulière. Lorsque vous travaillez en mode interprété, vous pouvez indifféremment passer une chaîne non vide ou vide à cette fonction.
En compilé, vous ne pouvez pas passer une chaîne vide.
Si vous le faites, le compilateur ne peut détecter une erreur à la compilation si l’argument passé à Character code est une variable.

SEND VARIABLE(LaVariable)
RECEIVE VARIABLE(LaVariable)

Ces deux commandes permettent d’écrire et de relire des variables envoyées sur disque. On passe des variables en paramètres à ces commandes.
Souvenez-vous simplement qu’il faudra toujours relire une variable d’un type dans une variable de même type. Supposons que vous souhaitiez envoyer une liste de variables dans un fichier. Afin de ne pas prendre de risque de changement de type par inattention, nous vous recommandons d’adopter une méthode de travail simple qui consiste à indiquer en début de liste le type des variables envoyées. Ainsi, lorsque vous relirez ces variables, vous commencerez toujours par récupérer cet indicateur dont vous connaissez le type. Ensuite, vous appellerez RECEIVE VARIABLE en toute connaissance de cause par l’intermédiaire d’un Au cas ou.

Exemple :
 SET CHANNEL(12;"LeFichier")
 If(OK=1)
    $Type:=Type([Client]CA_Cumulé)
    SEND VARIABLE($Type)
    For($i;1;Records in selection)
       $CA_Envoi:=[Client]CA_Cumulé
       SEND VARIABLE($CA_Envoi)
    End for
 End if
 SET CHANNEL(11)
 SET CHANNEL(13;"LeFichier")
 If(OK=1)
    RECEIVE VARIABLE($Type)
    Case of
       :($Type=Is string var)
          RECEIVE VARIABLE($LaChaine)
  `Traitement de la variable reçue
       :($Type=Is real)
          RECEIVE VARIABLE($LeRéel)
  `Traitement de la variable reçue
       :($Type=Is text)
          RECEIVE VARIABLE($LeTexte)
  `Traitement de la variable reçue
    End case
 End if
 SET CHANNEL(11)

Field(Ptr_Champ) ou (NoDeTable;NoDeChamp)
Table(Ptr_Table) ou (Ptr_Champ) ou (NoDeTable)

Dans l’optique du compilateur, ces deux commandes ne comportent rien de spécifique. Nous appelons simplement votre attention sur un cas pratique : ces deux commandes retournent des résultats de type différent suivant le paramètre qui leur est passé :

  • si vous passez un pointeur à la fonction Table, le résultat retourné sera de type Numérique.
  • si vous passez un Numérique à la fonction Table, le résultat retourné sera de type Pointeur.
    On comprend aisément que ces deux fonctions ne suffisent pas au compilateur pour déterminer le type du résultat. Dans ce cas, pour qu’il n’y ait aucune ambiguïté, utilisez une directive de compilation.

Nous rappellerons simplement que la référence d’un document renvoyée par les fonctions Open document, Append document et Create document est de type Heure.

Mod(LaValeur;Diviseur)

L’expression “25 modulo 3” peut s’écrire de deux façons différentes dans 4D :

 LaVariable:=Mod(25;3)

ou

 LaVariable:=25%3

Il existe pour le compilateur une différence entre ces deux écritures : Mod s’applique à tous les types de numériques tandis que l’opérateur % s’applique exclusivement aux Entiers et Entiers longs (si les opérandes de l’opérateur % dépassent les limites des Entiers longs, le résultat renvoyé sera probablement faux).

IDLE
ON EVENT CALL(LaMéthode {; NomProcess})
ABORT

Pour la gestion des interruptions, le langage de 4D dispose de la commande IDLE. Cette commande devra être utilisée lorsque vous vous servirez de la commande ON EVENT CALL.

On pourrait définir cette commande comme une directive de gestion des événements.
Seul le noyau de 4D peut détecter un événement Système (clic souris, action sur le clavier…). Dans la plupart des cas, des appels au noyau sont lancés par le code compilé lui-même, de façon transparente pour vous.

En revanche, dans le cas où vous attendez un événement sans rien faire, comme dans une boucle d’attente, il est bien évident qu’aucun appel n’est effectué.

  `Méthode projet ClicSouris
 If(MouseDown=1)
    <>vTest:=True
    ALERT("Quelqu’un a cliqué avec la souris")
 End if
 
  `Méthode projet Attente
 <>vTest:=False
 ON EVENT CALL("ClicSouris")
 While(<>vTest=False)
  `Boucle d’attente de l’événement
 End while
 ON EVENT CALL("")

Dans ce cas, vous ajouterez la directive IDLE de la façon suivante :

  `Méthode projet Attente
 <>vTest:=False
 ON EVENT CALL("ClicSouris")
 While(<>vTest=False)
    IDLE
  `Appel au noyau pour discerner un événement
 End while
 ON EVENT CALL("")
STOP  

Cette commande ne doit être utilisée que dans des méthodes projet d’interception d’erreurs. Cette commande fonctionne comme en mode interprété, sauf dans une méthode ayant été appelée par l’une des commandes suivantes :  EXECUTE FORMULA, APPLY TO SELECTION et _o_APPLY TO SUBSELECTION. Il convient d’éviter cette situation.

Sept routines de 4D sont utilisées par le compilateur pour déterminer le type d’un tableau. Il s’agit de :

COPY ARRAY(TableauSource;TableauDestination)
SELECTION TO ARRAY
(LeChamp;LeTableau)
ARRAY TO SELECTION
(LeTableau; LeChamp)
SELECTION RANGE TO ARRAY
(Début;Fin;LeChamp;LeTableau)
LIST TO ARRAY
(LaListe; LeTableau{;ItemRefs})
ARRAY TO LIST
(LeTableau; LaListe{;ItemRefs})
DISTINCT VALUES
(LeChamp;LeTableau)

COPY ARRAY admet deux paramètres de type Tableau. Lorsque le compilateur rencontre cette commande pendant le typage et que l’un des paramètres tableau n’est pas déclaré ailleurs, le compilateur déduit le type du tableau non déclaré suivant le type de celui qui l’est.
Cette déduction est faite dans les deux cas suivants :

  • le tableau typé ailleurs est le premier paramètre. Le compilateur donne au second tableau le type du premier.
  • le tableau déclaré est le second paramètre. Dans ce cas, le compilateur donne au premier tableau le type du second.

Le compilateur étant rigoureux sur les types, un COPY ARRAY ne peut se faire que d’un tableau d’un type vers un tableau de même type.
En conséquence, si vous souhaitez faire une copie d’un tableau d’éléments de types voisins, c’est-à-dire les Entiers, Entiers longs et Numérique ou les Textes et les Alphas ou les Alphas de longueurs différentes, vous devrez le faire élément par élément.

Imaginez que vous vouliez faire une copie d’un tableau d’Entiers vers un tableau de Numériques. Procédez comme suit :

 $Taille:=Size of array(TabEntier)
 ARRAY REAL(TabRéel;$Taille)
  `Donnons la même taille au tableau de Réels qu’au tableau d’Entiers
 For($i;1;$Taille)
    TabRéel{$i}:=TabEntier{$i}
  `Recopions chacun des éléments
 End for


Souvenez-vous que vous ne pouvez changer le nombre de dimensions d’un même tableau en cours de route. Vous provoqueriez une erreur de typage en copiant un tableau à une dimension dans un tableau à deux dimensions.

De même que pour 4D en mode interprété, ces quatre commandes n’exigent pas de déclaration de tableau. Le tableau non déclaré recevra le même type que le champ spécifié dans la commande.
Si vous écrivez :

 SELECTION TO ARRAY([LaTable]ChampEntier;LeTableau)

le type de LeTableau sera donc Tableau d’Entiers à une dimension.

Dans le cas où le tableau a déjà été déclaré, veillez à ce que les champs soient du même type. Bien qu’Entier, Entier long et Réel soient des types voisins, vous ne pouvez pas supposer qu’il soient équivalents.
Vous avez en revanche plus de latitude lorsqu’il s’agit des types Texte et Alpha. Par défaut, lorsqu’un tableau n’a pas été déclaré au préalable et que vous appliquez l’une de ces commandes avec pour paramètre un champ de type Alpha, le type attribué au tableau sera Texte. Si le tableau a été déclaré auparavant comme étant de type Alpha ou de type Texte, ces commandes respecteront vos directives.

Il en est de même dans le cas des champs de type Texte : vos directives sont prioritaires.
Souvenez-vous que les commandes SELECTION TO ARRAY, SELECTION RANGE TO ARRAY, ARRAY TO SELECTION et DISTINCT VALUES ne s’appliquent qu’à des tableaux à une dimension.

La commande SELECTION TO ARRAY admet aussi une seconde syntaxe :
SELECTION TO ARRAY(LaTable;LeTableau)
Dans ce cas, la variable LeTableau sera de type Tableau d’Entiers longs. Il en est de même pour la commande SELECTION RANGE TO ARRAY.

Les commandes LIST TO ARRAY et ARRAY TO LIST ne concernent que deux types de tableaux :

  • les tableaux Alpha à une dimension,
  • les tableaux Texte à une dimension.
    Ces deux commandes n’exigent pas la déclaration du tableau qui leur est passé en paramètre. Par défaut, un tableau non déclaré recevra le type Texte. Si le tableau a été déclaré auparavant comme étant de type Alpha ou de type Texte, ces commandes respecteront vos directives.

Le compilateur ne peut pas, durant le typage ou la compilation, détecter un conflit de type dans le cas où vous utiliseriez des pointeurs dépointés comme paramètre de commande de déclaration d’un tableau. Si vous écrivez :

 SELECTION TO ARRAY([LaTable]LeChamp;LePointeur->)

où LePointeur-> représente un tableau, le compilateur ne peut vérifier que le type du champ et du tableau sont identiques. Il vous appartient d’éviter alors ce type de conflit.

Pour cela, le compilateur vous fournit une aide précieuse : les Warnings. Chaque fois qu’il rencontre une routine de déclaration de tableau dans laquelle l’un des paramètres est un pointeur, il vous délivre un message vous invitant à la vigilance.

Si vous souhaitez compiler une base de données qui utilise des tableaux locaux (tableaux visibles uniquement par les méthodes qui les ont créés), il est nécessaire de les déclarer explicitement dans 4D avant de les utiliser.

La déclaration explicite d’un tableau signifie l’utilisation d’une commande de type  ARRAY REAL, ARRAY INTEGER, etc.

Par exemple, si une méthode génère un tableau local d’entiers contenant 10 valeurs, vous devez écrire au préalable la ligne suivante :

 ARRAY INTEGER($MonTableau;10)

Langage  

Get pointer(NomVariable)
Type
(Objet)
EXECUTE FORMULA
(Instruction)
TRACE

NO TRACE

Get pointer est une fonction qui retourne un pointeur sur le paramètre que vous lui avez passé. Supposons que vous vouliez initialiser un tableau de pointeurs. Chacun des éléments de ce tableau pointe vers une variable donnée. Ces variables sont au nombre de douze et nous les appelons V1, V2, …V12. Vous pourriez écrire :

 ARRAY POINTER(Tab;12)
 Tab{1}:=->V1
 Tab{2}:=->V2
 
 Tab{12}:=->V12

Vous pouvez aussi écrire :

 ARRAY POINTER(Tab;12)
 For($i;1;12)
    Tab{$i}:=Get pointer("V"+String($i))
 End for

A la fin de l’exécution de cette séquence, vous récupérez un tableau de pointeurs dont chaque élément pointe sur une variable Vi.

Ces deux séquences sont bien entendu compilables. Toutefois, si les variables V1 à V12 ne sont pas utilisées explicitement ailleurs dans la base, le compilateur ne pourra pas les typer. Il vous faut donc les nommer ailleurs explicitement.
Cette déclaration explicite peut se faire de deux façons :

  • soit en déclarant V1, V2, …V12 par une directive de compilation :
 C_LONGINT(V1;V2;V3;V4;V5;V6;V7;V8;V9;V10;V11;V12)
  • soit en affectant ces variables dans une méthode :
 V1:=0
 V2:=0
 
 V12:=0

Chaque variable de la base compilée n’ayant qu’un seul type, la fonction Type peut sembler d’un intérêt limité. Elle est cependant très précieuse lorsqu’on travaille avec des pointeurs. En effet, il peut être nécessaire de connaître le type de la variable pointée par un pointeur, la souplesse des pointeurs faisant que l’on ne sait pas toujours sur quoi ils pointent.

La commande EXECUTE FORMULA, historique dans 4D, présente des avantages en mode interprété qui n’ont pas grand sens en mode compilé.
En effet, en mode compilé, le nom de la méthode passé en paramètre à cette commande sera simplement interprété. Vous ne bénéficierez donc pas de tous les avantages apportés par le compilateur, sans compter que la syntaxe de votre paramètre ne pourra pas être vérifiée.
De surcroît, vous ne pouvez pas lui passer des variables locales comme paramètres.
On peut avantageusement remplacer un EXECUTE FORMULA par une série d’instructions. Nous vous en donnons ici deux exemples.

Soit la séquence suivante :

 i:=FctFormulaire
 EXECUTE FORMULA("FORM FIXER ENTREE (Formulaire"+String(i)+")")

Elle peut être remplacée par :

 i:=FctFormulaire
 VarFormulaire:="Formulaire"+String(i)
 FORM SET INPUT(VarFormulaire)

Examinons maintenant un deuxième cas :

 $Num:=ChoixImprimante
 EXECUTE FORMULA("Impri"+$Num)

Ici, l’EXECUTE FORMULA  peut être facilement remplacé par un Au cas ou :

 Case of
    :($Num=1)
       Impri1
    :($Num=2)
       Impri2
    :($Num=3)
       Impri3
 End case

La commande EXECUTE FORMULApeut toujours être avantageusement remplacée. En effet, la méthode à exécuter est prise dans la liste des méthodes projet de la base ou des commandes 4D, qui sont en nombre limité. En conséquence, il est toujours possible de remplacer la commande EXECUTE FORMULAsoit par un Au cas ou, soit par une autre commande.

Ces deux commandes, précieuses en mode interprété, n’ont pas de fonction en mode compilé. Vous pouvez cependant les laisser dans vos méthodes : TRACE et NO TRACE seront simplement ignorées par le compilateur.

Undefined(LaVariable)
SAVE VARIABLES
(NomduDoc;Variable1{; Variable2…})
LOAD VARIABLES
(NomduDoc;Variable1{; Variable2…})
CLEAR VARIABLE
(LaVariable)

Compte tenu du processus de typage par le compilateur, une variable ne peut à aucun moment être indéfinie en mode compilé. En effet, toutes les variables ont une existence dès la fin de la compilation. La fonction Undefined retourne donc toujours Faux, quel que soit le paramètre que vous lui passez.

Note : Pour savoir si une application tourne en mode compilé, utilisez la fonction Is compiled mode.

En mode interprété, après l’exécution d’un LOAD VARIABLES, vous pouvez savoir si le document existe en testant si l’une des variables, théoriquement lue, est indéfinie ou non. Ceci n’est bien évidemment plus possible avec le compilateur, puisque la fonction Undefined renvoie toujours Faux.

La méthode la plus simple pour réaliser cette opération en mode interprété comme en mode compilé est la suivante :
1. Initialisez les variables que vous allez recevoir à une valeur dont vous êtes sûr qu’elles ne sont pas initialisées.
2. Après le LOAD VARIABLES, comparez l’une des variables reçues à la valeur d’initialisation.
La méthode s’écrira alors de la façon suivante :

 Var1:="xxxxxx"
  `"xxxxxx" est une valeur qui ne peut pas être ramenée par un LIRE VARIABLES
 Var2:="xxxxxx"
 Var3:="xxxxxx"
 Var4:="xxxxxx"
 LOAD VARIABLES("LeDocument";Var1;Var2;Var3;Var4)
 If(Var1="xxxxxx")
  `Le document n’a pas été trouvé
 
 Else
  `Le document a été trouvé
 
 End if

Cette routine admet deux syntaxes différentes en mode interprété :
CLEAR VARIABLE(LaVariable)
CLEAR VARIABLE("a")
En mode compilé, la première syntaxe, CLEAR VARIABLE(LaVariable), provoque non la destruction de la variable, mais sa réinitialisation (remise à zéro pour un numérique, chaîne vide pour une chaîne de caractères ou un texte…), aucune variable ne pouvant être indéfinie en mode compilé.
En conséquence, CLEAR VARIABLE ne libère pas de mémoire en mode compilé sauf dans quatre cas : les variables de type Texte, Image, BLOB et les tableaux.
Pour un tableau, CLEAR VARIABLE équivaut à une nouvelle déclaration du tableau ou les tailles sont remises à zéro.

Pour un tableau LeTableau dont les éléments sont de type Entier, CLEAR VARIABLE(LeTableau) équivaut à l’une des expressions suivantes :

 ARRAY INTEGER(LeTableau;0)
  `s’il s’agit d’un tableau à une dimension
 ARRAY INTEGER(LeTableau;0;0)
  `s’il s’agit d’un tableau à deux dimensions

La seconde syntaxe, CLEAR VARIABLE("a"), n’est pas compatible avec le compilateur puisque celui-ci accède aux variables non par leur nom, mais par leur adresse mémoire.

Les commandes suivantes ont un point commun : elles admettent toutes un premier paramètre [LaTable] facultatif alors que le second paramètre peut être un pointeur.

ADD TO SETFORM SET INPUT
GOTO RECORDFORM SET OUTPUT
GOTO SELECTED RECORD_o_GRAPH TABLE
APPLY TO SELECTIONPRINT LABEL
LOAD SETPrint form
QUERYIMPORT TEXT
QUERY SELECTIONIMPORT DIF
QUERY BY FORMULAIMPORT SYLK
QUERY SELECTION BY FORMULARELATE MANY
COPY NAMED SELECTIONCREATE SET
CUT NAMED SELECTIONREDUCE SELECTION
DIALOGREMOVE FROM SET
EXPORT TEXTORDER BY
EXPORT DIFORDER BY FORMULA
EXPORT SYLKPAGE SETUP
CREATE EMPTY SETLOCKED BY
QR REPORT

Il est parfaitement possible en mode compilé de garder ce caractère optionnel au paramètre [LaTable]. Le compilateur fait toutefois une supposition dans le cas où le premier paramètre passé à l’une de ces commandes est un pointeur. Ne pouvant pas savoir sur quoi pointe ce pointeur, il suppose qu’il s’agit d’un pointeur de Table.

Prenons par exemple le cas de la commande QUERY dont la syntaxe est la suivante :
QUERY({LaTable{;LaFormule{;*}}})
Le premier élément du paramètre LaFormule doit être un champ.
Si vous écrivez :

 QUERY(LePtrChamp->=True)

le compilateur va chercher en deuxième élément de formule un symbole représentant un champ. Or, il trouvera le signe "=". Il délivrera un message d’erreur, ne pouvant identifier la commande avec une expression qu’il sait traiter.

En revanche, si vous écrivez :

 QUERY(LePtrTable->;LePtrChamp->=True)

ou

 QUERY([LaTable];LePtrChamp->=True)

vous levez toute ambiguïté.

Les commandes dont le premier paramètre [laTable] est facultatif et dont le second paramètre est également facultatif présentent une particularité lors de l'utilisation de pointeurs. Dans ce contexte, pour des raisons internes l'utilisation en tant que paramètre d'une commande retournant un pointeur (par exemple Current form table) n'est pas autorisée par le compilateur (une erreur est générée).

C'est le cas, par exemple, de la commande FORM SCREENSHOT. Le code suivant fonctionnera en mode interprété mais sera rejeté en cas de compilation :

  //provoque une erreur à la compilation
 FORM SCREENSHOT(Current form table->;$nomForm;$monImage)

Dans ce cas, il suffit d'utiliser une variable intermédiaire afin que le même code soit validé par le compilateur :

  //code équivalent compilable
 C_POINTER($ptr)
 $ptr:=Current form table
 FORM SCREENSHOT($ptr->;$nomForm;$monImage)

Si vous créez vos propres ressources 4DK# (constantes), assurez-vous que les numériques soient de type Entier long (L) ou Réel (R) et les alphas de type Chaîne (S). Tout autre type génèrera un Warning.



Voir aussi  

Conseils d’optimisation
Guide du typage
Messages d'erreurs
Utilisation des directives de compilation

 
PROPRIÉTÉS 

Produit : 4D
Thème : Compilateur

 
HISTORIQUE 

 
UTILISATION DE L'ARTICLE

4D - Langage ( 4D v16)
4D - Langage ( 4D v16.1)
4D - Langage ( 4D v16.2)
4D - Langage ( 4D v16.3)