home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The CDPD Public Domain Collection for CDTV 4
/
CDPD_IV.bin
/
e
/
amiga_e_v2.1b
/
docs-françaises
/
référence.doc
< prev
next >
Wrap
Text File
|
1994-05-26
|
117KB
|
3,242 lines
+-----------------------------------------------+
| |
| Amiga E v2.1b |
| Compilateur du Langage E |
| Par Wouter van Oortmerssen |
| |
| Référence du Langage |
| |
| Traduction : Olivier ANH |
| Membre du |
| Bordeaux User Group of Scientific Students |
| Copyright 1994 BUGSS |
| |
+-----------------------------------------------+
Contenu :
1. format
A. tabulations, saut de ligne (lf) etc.
B. commentaires
C. identificateurs et types
2. valeurs immédiates
A. décimales (1)
B. héxadecimales ($1)
C. binaires (%1)
D. flottans (1.0)
E. caractère ("a")
F. chaine ('bla')
G. listes ([1,2,3]) et listes typées
3. expressions
A. format
B. priorité des opérateurs
C. types d'expressions
D. appels de fonction
4. operateurs
A. mathématiques (+ - * /)
B. de comparaison (= <> > < >= <=)
C. logiques (AND OR)
D. unaire (SIZEOF ` ^ {} ++ -- -)
E. triple (IF THEN ELSE)
F. de structure (.)
G. de tableau ([])
H. flottants (|)
I. expressions d'assignement (:=)
J. séquence (BUT)
5. déclaration
A. format (;)
B. labels et gotos (JUMP)
C. assignement (:=)
D. mnémoniques assembleur
E. conditionel (IF)
F. for (FOR)
G. while (WHILE)
H. repeat (REPEAT)
I. loop (LOOP)
J. select-case (SELECT)
K. incrémentation (INC/DEC)
L. expressions vides (VOID)
6. fonction : définitions et déclarations
A. définition et arguments des procédures (PROC)
B. définitions locales et globales (DEF)
C. endproc/return
D. la fonction "main"
E. variables système prédéfinies (built-in)
7. déclaration de constantes
A. constantes (CONST)
B. énumerations (ENUM)
C. sets (SET)
D. constantes prédéfinies (built-in)
8. types
A. a propos du système "type"
B. letype de base (LONG/PTR)
C. le type simple (CHAR/INT/LONG)
D. le type tableau (ARRAY)
E. le type complexe (STRING/LIST)
F. le type composé (OBJECT)
G. initialisation
9. fonctions prédéfinies (built-in)
A. fonctions d'entrées-sorties (I/O)
B. chaines et fonctions de chaine
C. listes et fonctions de liste
D. fonctions de support Intuition
E. fonctions de support graphique
F. fonctions de support système
G. mathémathiques et autres fonctions
H. chaine et fonctions de lien entre listes
10. fonctions de bibliothèques (libraries) et modules
A. appels de bibliothèque prédéfinies (built-in)
B. interfacer le système amiga avec les modules 2.04
11. expressions cotées (`)
A. les cotes simple et l'analyse
B. Eval()
C. fonctions prédéfinies (built-in)
12. support des nombres flottants
A. utilisation des flottants et fonctions d'opérateurs flottants
B. expressions flottantes et conversions
13. Gestion des exceptions
A. définition des gestionnaires d'exceptions (HANDLE/EXCEPT)
B. utilisation de la fonction Raise()
C. définition des exceptions pour les fonctions prédéfinies (RAISE/IF)
D. utilisation des identificateurs d'exception
14. programmation orientée objet (OO)
15. assembleur inline
A. les identificateurs
B. l'assembleur en linge comparé au macro assembleur
C. comment utiliser les données binaires (INCBIN/CHAR..)
D. OPT ASM
16. implementations
A. le mot-clé OPT
B. le modèle small/large
C. l'organisation de la pile
D. les limites du code
E. les messages d'érreurs, d'avertissements et de non-référence
F. l'organisation des tampons du compilateur et allocation
G. un bref histotique
+---------------------------------------------------------------+
| 1. FORMAT |
+---------------------------------------------------------------+
1A. tabs,lf etc.
----------------
Les sources E sont de pures fichiers ASCII, avec saut de ligne <lf> et point-
virgule ";" servant de séparateurs de déclarations. Les déclarations, qui ont
des arguments particuliers, séparés par une virgule ",", peuvent s'étendre
sur plus d'une ligne terminée par une virgule, ignorant alors les sauts de
ligne.
1B. commentaires
----------------
Les commentaires peuvent être placés n'importe où dans le source où
normalement un espace aurait été correct. Ils commencent avec '/*' et se
terminent avec '*/' et peuvent être imbriqués les uns dans les autres.
1C. identificateurs et types
----------------------------
Les identificateurs sont des chaines que le programmeur utilise pour
caractériser certains objets, dans la plupart des cas des variables, ou même
des mots-clé ou des noms de fonction prédéfinies par le compilateur. Un
identificateur consiste en :
- caractères MAJUSCULEs et minuscules
- "0" .. "9" (sauf pour le premier character)
- "_" (le souligné)
Tous les caractères sont significatifs, mais le compilateur ne regarde que
les 2 premiers pour identifier le type d'identificateur avec lequel il a
affaire :
majuscule tous les deux : - mots-clé comme IF, PROC etc.
- constantes comme MAX_LENGTH
- mnémoniques assembleur comme MOVE
le premier en minuscule : - identificateurs de variable/label/objet etc.
premier en majuscule, second en minuscule :
- fonctions système du E system comme WriteF()
- appels de bibliothèque : OpenWindow()
Notez que tous les identificateurs obéissent à cette syntaxe, par exemple :
WBenchToFront() devient WbenchToFront()
+---------------------------------------------------------------+
| 2. VALEURS IMMEDIATES |
+---------------------------------------------------------------+
Les valeurs immédiates en E sont évaluées en 32 bits ; la seule différence
parmi ces valeurs (A-G) est soit leur représentation interne, ou le fait qu'
elles retournent un pointeur plutôt qu'une valeur.
2A. décimales (1)
-----------------
Une valeur décimales est un séquence de caractères "0"..."9", avec la
possibilité d'être précédé par un signe moins "-" pour les chiffres négatifs.
Exemples : 1, 100 ,-12, 1024
2B. héxadecimales ($1)
----------------------
Une valeurs héxadécimales utilise les caractères "A"..."F" en plus, ou
"a"..."f" et est précédées par un $.
Exemples:
$FC, $DFF180, -$ABCD
2C. binaires (%1)
-----------------
Le snmbres binaires commencent par un "%" et n'utilise que "0" et "1" pour
former une valeur.
Exemples: %111, %1010100001, -%10101
2D. flottants (1.0)
-------------------
Les flottants diffèrent des nombres décimaux de part le "." qui les séparent
en deux. Une des parties peut être omise, pas les 2.
Notez que le flottants ont une représentation interne de 32bits (FFP). Reportez
vous au chapitre 12 pour plus d'informations.
Exemples:
3.14159, .1 (=0.1), 1. (=1.0)
2E. caractères ("a")
---------------------
La valeur d'un caractère (encadré par des double cotes "") est leur valeurs
ASCII, par ex. "A" = 65. En E, les valeurs immédiates des caractères doivent
être des chaines d'au plus 4 caractères, par exemple "FORM", où le premier
caractère "F" sera le poids fort (MSB) de la représentation 32 bits, et "M"
le poids faible (LSB : least significant byte).
2F. chaines ('bla')
-------------------
Les chaines sont des représentations ASCII, encadrées pour des simple cotes.
La valeur de ces chaines est un pointeur sur le premier caractères.
Plus spécifique : 'bla' produit un pointeur de 32 bits dans la mémoire, où
on trouve les octets "b, "l", "a". TOUTES les chaines en E sont terminées
par un octet zéro (0).
Les chaines peuvent contenir des symboles de format introduit par "\" (anti
slash), soit pour introduire des caractères dans la chaine, qui sont pour
quelques raisons non affichables, soit pour formatter la chaine avec les
fonctions de format telles que WriteF(), TextF() and StringF(), ou
sous kick2.0, Vprintf().
\n saut de ligne (ascii 10)
\a or '' apostrophe ' (celui pour encadrer une chaine)
\e escape (ascii 27)
\t tabulation (ascii 9)
\\ barre oblique inverse
\0 octet zéro 0. D'utilisation rare, car les chaines
finissant toutes par un zéro.
\b retour chariot (carriage return) (ascii 13)
De plus, quand utilisé avec les fonctions de format :
\d affiche un nombre décimal
\h affiche un nombre héxadecimal
\s affiche une chaine
\c affiche un caractère
\z met l'octet de remplissage à '0'
\l justifie le champ à gauche
\r justifie le champ à droite
Les spécificateurs de champs doivent suivrent les codes \d, \h et \s :
[x] specifie un champ de largeur x
(x,y) specifie le x minimum et le y maximum (chaine seulement)
Exemple: affiche un nombre héxadécimal avec 8 caractères laissant la place
aux zéros :
WriteF('\z\h[8]\n',num)
Une chaine peut-être étendue sur plusieurs ligne en insérant entre eux un "+"
et un saut de ligne (lf) :
'Ceux sont 2 longues lignes ' +
'qui sont mises sur 2 lignes'
2G. listes ([1,2,3]) et listes typées
-------------------------------------
Une liste immédiate est la partie constante du type LISTe, comme une 'chaine'
est la partie constante pour une chaine ou un tableau de cractères (ARRAY OF
TYPE).
Exemple :
[3,2,1,4]
est une expression qui a comme vameur un PoinTeuR sur une liste initialisée,
une liste comme représentation en mémoire est compatible avec un tableau
(ARRAY OF LONG), avec quelques informations en plus en offset négatif. Vous
devez utiliser ces listes immédiates n'importe où, où une fonction demande un
pointeur sur un tableau de valeurs 32bits, ou une liste.
Exemple :
['chaine',1.0,2.1]
[WA_FLAGS,1,WA_IDCMP,$200,WA_WIDTH,120,WA_HEIGHT,150,TAG_DONE]
Voir la partie sur les fonctions de liste pour différencier les listes
immédiates et typées, et pour des informations détailées.
+---------------------------------------------------------------+
| 3. EXPRESSIONS |
+---------------------------------------------------------------+
3A. format
----------
Une expression est un morceau de code lié par des opérateurs, fonctions et
parenthèses pour former une valeur.
Il consiste dans la plupart des cas en :
- des valeurs immédiates (chapitre 2)
- des opérateurs (chapitre 4)
- des appels de fonction (chapitre 3D)
- des parenthèses () (chapitre 3B)
- des variables ou des expressions-variables (chapitre 3C)
Exemples d'expressions :
1
'hello'
$ABCD+(2*6)+Abs(a)
(a<1) OR (b>=100)
3B. priorité des opérateurs
---------------------------
Le langage E n'a pas de priorité. Cela signifie que les expressions
sont évaluées de gauche vers la droite. Vous pouvez changer cette priorité
en mettant entre parenthèses des (sous-)expressions :
1+2*3 /* =9 */ 1+(2*3) /* =7 */ 2*3+1 /* =7 */
3C. types d'expressions
-----------------------
Il y a 3 types d'expressions qui peuvent être utilisés pour différentes
applications :
- <var>, juste une variable
- <varexp>, un variable, avec un opérateurs unaire possible
comme ++ (incrémentation) ou [] (opérateur de tableau). Pour cela,
voir les chapitre 4D et 4G. Il cractérise un expression modifiable
comme Lvaleur en C.
Notez que ces opérateurs unaires ne font pas partie des priorité.
- <exp>. Cela inclue <var> et <varexp>, et n'importe quelle autre
expression.
3D. appels de fonction
----------------------
Un appel de fonction est arrêt temporaire du code pour un saut vers une
fonction, qui peut-être une procédure (PROC), ou juste une fonction du
système. Le format d'un appel de fonction est le nom de la fonction, suivit
par 2 parenthèses () encadrant de zéro à un nombre illimité d'argument,
séparés par des virgules ",".
Notez que les arguments de fonctions sont des expressions.
Voir le chapitre 6 pour savoir comment faire sa propre fonction, et chapitre
9 et 10 pour les fonctions prédéfinis.
Exemples :
foo(1,2)
Gadget(buffer,glist,2,0,40,80+offset,100,'Cancel')
Close(handle)
+---------------------------------------------------------------+
| 4. OPERATEURS |
+---------------------------------------------------------------+
4A. mathématiques (+ - * /)
---------------------------
Ces opérateurs combinent une expression à un autre valeur pour produire une
nouvelle valeur.
Exemples :
1+2, MAX-1*5
Voir le chapitre 12 pour savoir comment utiliser les opérateurs sur les
flottants.
Le "-" peut être utilisé en première d'expression, avec un 0 implicite,
par ex. -a ou -b+1 sont egaux.
Notez que * et / sont par défaut des opérateurs 16 bits : Voir Mul()
4B. comparaison (= <> > < >= <=)
--------------------------------
Idem aux opérateurs mathématiques, à la différence qu'ils renvoient soit VRAI
(TRUE, valeur 32 bits = -1), ou FAUX (FALSE =0). Ils peuvent être surpassé
par les flottants.
4C. logique (AND et OR)
-----------------------
Ces opérateurs soit combinent des valeurs vraies en de nouvelles, soit
réalisent des opérateurs sur des bits AND et OR.
Exemples :
(a>1) AND ((b=2) OR (c>=3)) /* logique */
a:=b AND $FF /* opérateur de bits */
4D. unaire (SIZEOF ^ {} ++ -- `)
--------------------------------
- SIZEOF <objet>
retourne simplement la taille d'un objet.
Exemple: SIZEOF newscreen
- {<var>}
retourne l'adresse d'une variable ou d'un label. C'est cet opérateur que
vous utiliserez pour donner une variable comme argument à une fonction par
référence, et non pas par valeur, ce qui est le défaut en E. Voir "^".
Exemple: Val(input,{x})
- ^<var>
A l'inverse de {}, il écrit ou lit des variables qui sont donnés par
référence.
Examples: ^a:=1 b:=^a
Il est alors utilisé pour aller chercher (peek) et poser (poke) des valeurs
LONG en mémoire, si <var> est un pointeur sur cette valeur.
Exemple pour {} et ^: écrit votre fonction d'assignement :
PROC set(var,exp)
^var:=exp
ENDPROC
et appelez le avec : set({a},1) /* egale a:=1 */
- <varexp>++ et <varexp>--
Incréménte (++) ou décrémente (--) le pointeur caractérisé par <varexp> par
par la taille des données vers lequel il pointe. Cela a pour effet que ce
pointeur pointe vers l'article (item) suivant ou précédent. Quand il est
utilisé sur des variables qui ne sont pas des pointeurs, il changera
simplement en 1. Notez que ++ prend effet _après_ le calcul de <varexp>, et
-- toujours _avant_.
Exemples :
a++ /* retourne la valeur de a, et l'incrémente de 1 */
sp[]-- /* décrémente le pointeur sp de 4 (si il y avait un tableau),
et lit la valeur pointée par sp */
- `<exp>
On appelle un telle expression, un expression cotée, venant du LISP. <exp>
n'est pas évaluée, mais retourne l'adresse de l'expression, qui peut être
plus tard évaluéeau moment voulu. Voir le chapitre 11 pour plus
d'informations.
4E. triple (IF THEN ELSE)
-------------------------
L'opérateur IF a presque la même fonction que la déclaration IF, seulement il
choisit entre 2 expressions au lieu de 2 déclarations ou blocs de code.
Il est équivalant à l'opérateur x?y:z en C.
IF <boolexp> THEN <exp1> ELSE <exp2>
retourne exp1 ou exp2, suivant l'expression booléenne boolexp. Par exemple,
au lieu de :
IF a<1 THEN b:=2 ELSE b:=3
IF x=3 THEN WriteF('x égale 3\n') ELSE WriteF('x égale autre chose\n')
écrivez:
b:=IF a<1 THEN 2 ELSE 3
WriteF(IF x=3 THEN 'x égale 3\n' ELSE 'x égale autre chose\n')
4F. structures (.)
-----------------
<ptr2object>.<memberofobject> forme une <varexp>
Le pointeur doit être déclaré comme PTR TO <objet> ou ARRAY OF <objet>
(voir le chapitre 8 pour cela), et le membre doit être un identificateur
d'un objet déclaré. Notez que lire un sous objet d'un objet comme cela
donne un pointeur sur cet objet.
Exemple :
cettetache.userdata:=1
rast:=myscreen.rastport
4G. tableaux ([])
-----------------
<var>[<indexexp>] (est une <varexp>)
Cet opérateur lit la valeur d'un tableau sur lequel <var> pointe, et avec
pour index <indexexp>. Cet index peut être simplement une expression, avec un
petite limitation : il ne doit pas contenir des appels de fonction quand
utilisé sur la partie gauche de l'assignement.
Note 1 : "[]" est un raccourcit de "[0]"
Note 2 : avec un tableau de n éléments, l'index va de 0 .. n-1
Exemples:
a[1]:=10 /* met le second élément à 10 */
x:=table[y*4+1] /* lit le tableau */
4H. opérateurs flottants (|)
----------------------------
<exp>|<exp>
Convertis des expressions d'entier en flottants et inversement, et surpasse
les opérateurs + - * / = <> < > <= >= avec les équivalents flottants.
Voir le chapitre 12 pour tout savoir sur les flottants et cet opérateur.
4I. expressions d'assignements (:=)
-----------------------------------
Les assignements (donner une valeur a une variable) existe comme déclaration
et comme expression. La seule différence est que la déclaration est de la forme
<varexp>:=<exp> et l'expression <var>:=<exp>.
Ce dernier a la valeur <exp> comme résultats.
Notez que comme <var>:= prend une expression, vous aurez souvent besoin de
parenthèses pour forcer la bonne interprétation, comme :
IF mem:=New(100)=NIL THEN error()
est interprété comme :
IF mem:=(New(100)=NIL) THEN error()
ce qui n'est pas ce qu'on veut : mem doit être un pointeur, pas un booléen.
On doit écrire :
IF (mem:=New(100))=NIL THEN error()
4J. séquence (BUT)
-------------------
L'opérateur de séquence "BUT" vous permet d'écrire 2 expressions dans une
construction n'en acceptnt qu'une seule. Souvent en écrivant de complexes
appels de fonction, on voudrait faire une autre action 'à la volée', comme
un assignement :
<exp1> BUT <exp1>
Cela veux dire : évalue exp1, mais retourne la valeur de exp2.
Exemple:
mafonction((x:=2) BUT x*x)
assigne 2 à x, et appelle la fonction mafonction avec x*x. Les () autour
de l'assignementsont nécessaire pour prévenir l'opérateur := qu'il s'agit
d'une expression.
+---------------------------------------------------------------+
| 5. DECLARATIONS |
+---------------------------------------------------------------+
5A. format (;)
--------------
Comme suggéré au chapitre 1A, une déclaration est en général mise sur sa
propre ligne, mais plusieurs peuvent être mise ensemble sur une seule ligne,
en les séparant par un point-virgule ";", ou bien une déclaration peut être
étendue sur plus d'une ligne, celles-ci finissant par une virgule.
Exemples :
a:=1; WriteF('salut!\n')
DEF a,b,c,d, /* trop d'arguments sur la ligne */
e,f,g
Les déclarations peuvent être :
- des assignements
- des déclarations conditionelles, voir aussi les chapitres 5E-5K
- des expressions vides
- des labels
- des instructions assembleurs
La virgule est le premier caractère montrant uevous ne voulez pas terminer la
déclaration avec le prochain saut de ligne (lf), mais les caractères suivants
signalent la suite de la déclaration sur la ligne suivante :
+ - * /
= > < <> >= <=
AND OR BUT THEN
5B. déclarations de labels et gotos (JUMP)
------------------------------------------
Les labels sont des identificateurs globals avec un ":" à la fin :
monlabel:
Ils peuvent être utilisés par des instructions comme JUMP, et comme référence
de données statiques. Ils peuvent être utilisés pour sortir de tous types de
boucles (cette technique n'est pas recommandée), mais pas en dehors de
procédures. Dans un programme E normal, ils sont souvent utilisés dans les
lignes assembleurs.
Les labels sont toujours très visible.
JUMP <label>
continue l'éxécution à prtir du <label>. Vous n'êtes pas encouragé à utiliser
cette instruction, elle est là pour des situations qui devrait augmenter la
compléxité du programme si elle n'était pas là.
Exemple :
IF Mouse()=1 THEN JUMP stopmaintenant
/* suite du programme */
stopmaintenant:
5C. assignement (:=)
--------------------
Le format de base d'un assignement est : <var> := <exp>
Exemples:
a:=1
a:=mafonction()
a:=b*3
5D. mnémoniques assembleurs
---------------------------
En E, les lignes assembleurs font partie intégrante du langage, elles n'ont
pas besoin d'être encadré dans un blocs "ASM" spécial, comme il est usuel
dans d'autres langages, ni être assemblé par un assembleur externe pour
assembler le code. Cela veut dire qu'elles obéissent aux règles de syntaxe
du E, etc. Voir le chapitre 15 pour plus de détails.
Exemple :
DEF a,b
b:=2
MOVEQ #1,D0 /* quelques lignes assembleurs */
MOVE.L D0,a /* a:=1+b */
ADD.L b,a
WriteF('a=\d\n',a) /* a sera égal à 3 */
5E. déclaration conditionelle (IF)
----------------------------------
IF, THEN, ELSE, ELSEIF, ENDIF
syntaxe: IF <exp> THEN <déclaration> [ ELSE <instruction> ]
ou: IF <exp>
<instructions>
[ ELSEIF <exp> /* plusieurs elseif peuvent */
<instructions> ] /* avoir lieu */
[ ELSE ]
<instructions>
ENDIF
construit un bloc conditionnel. Notez qu'il y a 2 formes générales de cette
déclaration, en ligne unique et en ligne étendue.
5F. instruction for (FOR)
-------------------------
FOR, TO, STEP, DO, ENDFOR
syntaxe: FOR <var> := <exp> TO <exp> STEP <pas> DO <instruction>
ou: FOR <var> := <exp> TO <exp> STEP <pas>
<instructions>
ENDFOR
construit une boucle for. Notez les 2 formes générales. <pas> est une
constante positive ou négative, à l'exclusion de 0.
Exemple :
FOR a:=1 TO 10 DO WriteF('\d\n',a)
5G. intruction while (WHILE)
----------------------------
WHILE, DO, ENDWHILE
syntaxe: WHILE <exp> DO <instruction>
ou: WHILE <exp>
<instructions>
ENDWHILE
construit une boucle while, qui est répétée tant que <exp> est vraie (TRUE).
Notez les 2 formes générales.
5H. instruction repeat (REPEAT)
-------------------------------
REPEAT, UNTIL
syntaxe: REPEAT
UNTIL <exp>
contruit un block en boucle repeat-until : la boucle sera répétée jusqu'à ce
que <exp> soit vraie (TRUE) .
Exemple :
REPEAT
WriteF('Vous voulez vraiment sortir du programme ?\n')
ReadStr(stdout,s)
UNTIL StrCmp(s,'oui !')
5I. instruction loop (LOOP)
---------------------------
LOOP, ENDLOOP
syntaxe: LOOP
<instructions>
ENDLOOP
construit une boucle infinie.
5J. instruction select-case (SELECT)
------------------------------------
SELECT, CASE, DEFAULT, ENDSELECT
syntax:e SELECT <var>
[ CASE <exp>
<intructions> ]
[ CASE <exp>
<instructions> ] /* autant de bloc de ce type */
[ DEFAULT
<instructions> ]
ENDSELECT
construit un bloc select-case. De nombreuses expressions vont être comparées
à la variable, et seulement le premier bon bloc sera éxécuté. Si rien n'est
trouvé, le bloc par défaut est éxécuté.
SELECT caractère
CASE 10
WriteF('He, j'ai trouvé un saut de ligne\n')
CASE 9
WriteF('Ouah, ca doit être une tabulation!\n')
DEFAULT
WriteF('Vous connaissez celui là : \c ?\n',caractère)
ENDSELECT
5K. instruction d'incrémentation (INC/DEC)
------------------------------------------
INC, DEC
syntaxe: INC <var>
DEC <var>
raccourcit de <var>:=<var>+1 et <var>:=<var>-1. La seule différence avec
<var>++ et <var>-- est que INC et DEC sont des instructions, etne retourne
aucune valeurs, et sont plus efficient.
5L. expressions vide (VOID)
---------------------------
VOID
syntaxe: VOID <exp>
calcule l'expression sans que le résultat n'aille quelque part. Utile
seulement pour une syntaxe claire, comme les expressions peuvent être
utilisées comme intruction sans VOID en E ! Cela peut provoquer une subtile
érreur : alors que "a:=1" assigne à a la valeur 1, "a=1" est une instruction
qui ne fait rien. E vous le signalera si cela arrive.
+---------------------------------------------------------------+
| 6. DEFINITIONS DE FONCTIONS ET DECLARATIONS |
+---------------------------------------------------------------+
6A. définition et arguments des procédures(PROC)
------------------------------------------------
Vous devez utiliser PROC et ENDPROC pour définir les instructions dans vos
propres fonction. Ces fonctions peuvent avoir plusieurs arguments, mais
retourne une seule valeur.
syntaxe: PROC <label> ( <args> , ... )
ENDPROC <valeur_retournée>
définie une procédure avec un nombre d'arguments. Les arguments sont de type
LONG ou de type PTR TO <type> (voir chapitre 8) et n'ont pas besoin d'autre
déclaration.
La fin de la procédure est désigné par ENDPROC. Si aucune valeur n'est donnée,
0 est retournée.
Exemple : écrire une fonction qui retourne la somme de 2 arguments :
PROC add(x,y) /* x et y sont des variables locales */
ENDPROC x+y /* retourne le résultat */
6B. définitions locales and globales (DEF)
------------------------------------------
Vous devez définir des variables locales en plus de celles qui sont définies
avec DEF. Le plus simple est :
DEF a,b,c
déclare les identificateurs a, b et c de votre fonction.
Notez que ces déclarations doivent être au début de votre fonction.
syntaxe: DEF <déclarations>,...
description: declare les variables. Une déclaration est une de ces formes:
<var>
<var>:<type> where <type>=LONG,<objet>
<var>[<taille>]:<type> where <type>=ARRAY,STRING,LIST
Voir le chapitre 8 pour plus d'exemples, c'est là que les type sont introduit.
A partir de maintenant, on utilisera l'écriture <var>.
Les arguments de fonctions sont réduit aux type simple : voir chapitre 8B.
Une déclaration de type simple peut avoir une initiaisation. Dans cette
version elle doit être faite par un entier (pas d'expression) :
DEF a=1,b=2
Un programme consiste en une suite de fonctions, appellé procédures, PROC.
Chacune d'elles puevent avoir des variables locales, et le programme en son
entier, peut avoir des variables globales. Enfin, une procédure doit être
un PROC main(), car c'est cette procédure qui est éxécutée en premier. Un
programme simple peut ressembler à ça :
DEF a, b /* définition des variables globales */
PROC main() /* l'ordre des fonctions peut être */
bla(1) /* mis au hasard */
ENDPROC
PROC bla(x)
DEF y,z /* possibilité de variables locales */
ENDPROC
Pour résumer, les définitions locales sont celles que vous faites au début
des procédures, et qui ne sont visible qu'à l'intérieure de celles-ci.
Les définitions globales sont faites avant le premier PROC, au début de votre
source, et ils sont visibles dans toutes les procédures.
Les variables globales et locales (et bien sûr les variables locales de 2
différentes fonctions) peuvent avoir le même nom ; les variables locales ont
tjours la priorité sout les variables globales.
6C. endproc/return
------------------
Comme décrit plus haut, ENDPROC marque la fin d'une définition de fonction, et
peuvent retourner une valeur. De plus RETURN peux être utilisé n'importe où
dans la fonction pour en sortir. Si il est utilisé dans la procédure main(),
on sortira du programme. Voir aussi CleanUp() au chapitre 9F.
RETURN [<valeur_retournée>] /* optionel */
Exemple:
PROC getresources()
/* ... */
IF error THEN RETURN FALSE /* quelque s'est mal passé, on sort */
/* ... */
ENDPROC TRUE /* on est allé aussi loin, on retourne TRUE */
Une version très raccourcit d'un définition de fonction peu-être :
PROC <label> ( <arg> , ... ) RETURN <exp>
Ce sont des définitions de fonctions qui ont besoin de peu de calcul, comme
les fonctions de puissances et associé : (en une ligne :-)
PROC fac(n) RETURN IF n=1 THEN 1 ELSE fac(n-1)*n
6D. la fonction "main"
----------------------
La procédure appelée main a son importance, car elle est appelé en première ;
elle fonctionne de la même minère que les autres fontions, et peut donc avoir
des variables locales. Main n'a pas d'arguments : les arguments de la ligne
de commande (du shell) sont fournis par la variable système "arg", ou peuvent
être collectée avec ReadArgs().
6E. variables système prédéfinies (built-in)
--------------------------------------------
Les variables globales suivantes sont toujours accessible au sein du programme.
Ils sont appellés variables système :
arg Vu plus haut, arg contient un pointeur sur une chaine terminée
par un zéro. arg contient les arguments de la ligne de
commande. Ne l'utilisez pas si vous voulez utiliser ReadArgs().
stdout Contient le gestionnaire de fichier de la sortie et de
l'entrée standard. Si votre programme est lancé du workbench,
aucune sortie shell n'est possible. WriteF() ouvrira une
fenêtre CON: pour vous, et mettra son gestionnaire là.
conout Ces de là que le gestionnaire de fichier est pris et la
fenêtre console sera automatiquement fermée à la fin de votre
programme. Voir WriteF() dans le chapitre 9A pour savoir
comment utiliser proprement les variables.
execbase, Ces 5 variables sont toujours définies avec leur valeur
dosbase, correcte.
gfxbase,
intuitionbase,
mathbase
stdrast Pointeur sur le rastport standard utilisé dans votre
programme, ou bien NIL. Les fonctions graphique prédéfinies
comme Line() utilisent cette variable.
wbmessage Contient un pointeur sur un message reçu si le programme est
lancé du workbench, sinon NIL. Il peut être utilisé comme
variable booléenne pour détecter si vous vous êtes partis du
workbench, ou pour pour collecter les arguments d'éventuelles
icones cliquées. Voir le programme WbArgs.e dans le
répertoire sources/exemples, pour savoir comment bien
l'utiliser.
+---------------------------------------------------------------+
| 7. DECLARATION DE CONSTANTES |
+---------------------------------------------------------------+
7A. constantes (CONST)
----------------------
syntaxe: CONST <déclarations>,...
Vous permet de déclarer une constante. Une déclaration ressemble à :
<ident>=<valeur>
Les constantes doivent être en majuscule, et seront traité dans le reste du
programme comme <valeur>.
Exemple :
CONST MAX_LINES=100, ER_NOMEM=1, ER_NOFILE=2
Vous ne pouvez pas déclarer des constantes dans les mêmes termes qu'une autres
dans la même instruction CONST. Mettez le dans une autre.
7B. énumerations (ENUM)
-----------------------
Les énumérations sont un type de constante particulier, qui n'ont pas besoin
de valeur, car ils sont définis de 0 à n ; le premier étant égal à zéro. A
n'importe quel moment de l'énumération, vous pouvez utiliser la notation
'=<value>' pour fixer ou remettre à zéro le compteur.
Exemple :
ENUM ZERO, ONE, TWO, THREE, MONDAY=1, TUESDAY, WEDNESDAY
ENUM ER_NOFILE=100, ER_NOMEM, ER_NOWINDOW
7C. sets (SET)
--------------
Les sets sont du même type que les énumérations, à la différence qu'au lieu
d'augmenter leur valeur (0,1,2,...), on augmenter leur nombre de bit
(0,1,2,...) et donc leur valeur deviennent (1,2,4,8,...). Cela a pour avantage
qu'ils peuvent être utilisés comme des drapeaux (flags).
Supposez un set comme celui--ci pour décrire les propriété d'un fenétre :
SET SIZEGAD,CLOSEGAD,SCROLLBAR,DEPTH
pour initialiser une variable au pripriété DEPTH et SIZEGAD :
winflags:=DEPTH OR SIZEGAD
pour ajouter un SCROLLBAR :
winflags:=winflags OR SCROLLBAR
et pour tester si 2 propriétés sont pris en compte :
IF winflags AND (SCROLLBAR OR DEPTH) THEN /* ... */
7D. constantes prédéfinies (built-in)
-------------------------------------
Les constantes suivantes sont prédéfinies et peuvent être utilisées :
TRUE,FALSE Représente les valeurs booléennes (-1,0)
NIL (=0), le poineur non initialisé
ALL Utilisé avec les fonctions de chaine comme StrCopy() pour
copier tous les caractères
GADGETSIZE taille minimum en octets nécessaire pour définir un gadget ;
voir Gadget() au chapitre 9D
OLDFILE,NEWFILE Paramètres de mode à utiliser avec Open()
STRLEN A toujours la longueur de la dernière chaine utilisée.
Exemple :
Write(handle,'Salut tous le monde!',STRLEN) /* =20 */
+---------------------------------------------------------------+
| 8. TYPES |
+---------------------------------------------------------------+
8A. à propos du système "type"
------------------------------
Le E n'a pas une système de type rigid comme le Pascal ou le Modula2, il est
même plus flexible que clui du C : on doit plutôt l'appeler un système de type
de donnée (datatype system). Il en résulte qu'en E tous les types de données
sont égaux : toutes les valeurs simples comme les carctères, entiers, etc.
Ils ont tousla même taille de 32 bits, et les autres types comme les tableaux,
les chaines sont représentés par des pointeurs de 32 bits pointant sur eux.
Ainsi, le compilateur E peut générer un code de façon très polymorphe.
Les (dés)avantages sont nombreux :
désavantages :
- peu de vérification sur des érreurs que vous faites.
avantages :
- polymorphisme de bas niveau,
- programmation très flexible : aucune crainte de voir certaines valeurs
retournées, ne correspondant pas à la variable cible, etc.,
- facilité de retrouver des érreurs lors de mélange de données
d'expressions de taille différentes,
- bénéficie de types 'auto-documentés', si vous voulez comme ça :
PTR to newscreen
8B. le type simple (LONG/PTR)
-----------------------------
Il n'y a qu'un seul simple et non-complexe type en E, qui est le type
LONG de 32 bits. Comme c'est le type par défaut, il peut être déclaré comme :
DEF a:LONG ou juste: DEF a
Ce type de variable a les mêmes caractèristiques que les types
CHAR/INT/OTR/LONG d'autres langages. Un variation de LONG est le type pointeur
PTR. Ce type est compatible avec le LONG, à la différence qu'il est spécifié
en tant que pointeur. Par défaut, le type LONG est spécifié comme PTR TO CHAR.
Syntaxe: DEF <var>:PTR TO <type>
où le <type> est de type simple ou composé.
Exemple :
DEF x:PTR TO INT, mon_écran:PTR TO screen
Notez que 'screen' est le nom d'un objet défini par le module
'intuition/screens.m'. Par exemple, si vous ouvrez votre propre écran avec :
mon_écran:=OpenS(... etc.
vous devez utiliser le pointeur mon_écran tout comme 'mon_écran.rastport'.
Si vous ne voulez pas utiliser cette variable jusqu'à la fermeture de l'écran
par CloseS(mon_écran), vous pouvez simplement le déclarer comme :
DEF mon_écran
8C. le type simple (CHAR/INT/LONG)
----------------------------------
Les types simples CHAR (8 bits) et INT (16 bits) ne doivent pas être utilisés
pour des variables de base. La raison de cela doit être clair maintenant.
Malgré tout, ils peuvent être utilisés comme type de données pour construire
des tableaux, ou poser des pointeurs, ou être utilisé dans des objets, etc.
8D. le type tableau (ARRAY)
---------------------------
Les tableaux sont déclarés en spécifiant leur longueur en octets :
DEF b[100]:ARRAY
définie un tableaux de 100 octets. De façon interne, b est une variable de type
LONG et un pointeur sur cet espace mémoire.
Le type par défaut d'un élément du tableau est CHAR, mais peut être autre
chose en le spécifiant :
DEF x[100]:ARRAY OF LONG
DEF mes_menus[10]:ARRAY OF newmenu
où 'newmenu' est une exemple de structure (appelé OBJEcT en E).
L'accès au tableau est très simple :
<var>[<exp>]
Exemples :
b[1]:="a"
z:=mes_menus[a+1].mutualexclude
Notez que l'index d'un tableaux de taille n, est définis de 0 à n-1,
et pas de 1 à n.
Notez que les tableaux de type x (ARRAY OF <type>) est compatible avec les
pointeurs sur un type x (PTR TO <type>, à la différence que la variable
tableau est déja initialisée.
8E. le type complexe (STRING/LIST)
----------------------------------
- STRING (chaine). Similaire aux tableaux, mais différent dans le sens
qu'elles ne peuvent être modifiées que par les fonctions de chaines du E,
et qu'ils contiennent les informations de longueurs (length et maxlength).
Ainsi les fonctions de chaines peuvent les modifier sans crainte : la
chaine ne peut pas être plus grande que l'espace mémoire où elle est.
Syntaxe : DEF s[80]:STRING
Le type de données STRING est rétroactivement compatible avec PTR TO CHAR
et bien sûr ARRAY TO CHAR, et c'est tout.
Voir la chapitre sur les fonctions de chaines pour plus de détails.
- LIST (liste). C'est un type de données que l'on ne retrouve que dans
d'autres langages comme le Prolog ou le Lisp. La version E peut être
interprétée comme un mélange de chaine et de tableaux de LONG :
cette structure de données est une liste de variables LONG qui peuvent
être étendu ou résumé à des chaines.
Syntaxe : DEF x[100]:LIST
Un plus puissant à ce type de données est qu'il a un équivalent 'constant'
[], comme les chaines ont ''. Les listes sont rétroactivement compatible
avec PTR TO lONG et bien sûr ARRAY OF LONG, et c'est tout.
Voir les chapitres 2G et 9C pour plus d'informations.
8F. le type composé (OBJECT)
----------------------------
Les objets sont l'équivalent des structures en C ou RECORD en Pascal.
Exemple :
OBJECT myobj
a:LONG
b:CHAR
c:INT
ENDOBJECT
définie une structure de données de 3 éléments.
Syntaxe: OBJECT <nom_objet>
<nom_membre> [ : <type> ] /* autant que vous voulez */
ENDOBJECT
où le type est un type simple ou composé, ou un tableau simple :
[<nb_éléments>]:ARRAY avec par défaut, la taille CHAR pour un élément.
Notez que <nom_membre> peut ne pas être seul, et peut se trouver dans
d'autres objets. Il y a beaucoup de manière d'utiliser les objets :
DEF x:mon_objet /* x est une structure */
DEF y:PTR TO mon_objet /* y est juste un pointer dessus */
DEF z[10]:ARRAY OF mon_objet
y:=[-1,"a",100]:myobj /* listes typées */
IF y.b="a" THEN /* ... */
z[4].c:=z[d+1].b++
Les tableaux dans les objets sont toujours arrondis à la taille paire,
et mis sur des offsets pairs :
OBJECT machaine
longueur:CHAR,
donnée[9]:ARRAY
ENDOBJECT
La taille (SIZEOF) de 'machaine' est de 12, et 'donnée' commence à
l'offset 2.
NOTE : les objet du E ne sont pas ce que vous imaginez dans d'autres
langages. Par exemple, pas seulement les types peuvent former un membre
d'un objet, et grace à ça, des accès récursif aux objets comme x.y.z
non aucun sens (pour le moment).
8G. initialisation
------------------
1. Sont toujours initialisées à NIL (or autres, si explicitement décrit)
- les variables globales,
NOTE : pour une bonne documentation, il est conseillé d'écrire
xxx =NIL dans la définition de variables qui vous voulez avoir nulle
2. Sont initialisées a '' et respectivement []
- les chaines globales et locales
- les listes globales et locales
3. Ne sont pas initialisés
- les variables locales (tant qu'elle ne sont pas définies explicitement)
- les tableaux globals et locaux
- les objets globals et locaux
+---------------------------------------------------------------+
| 9. FONCTIONS PREDEFINIES |
+---------------------------------------------------------------+
9A. fonctions d'entrée - sortie (I/O)
-------------------------------------
WriteF(formatstring,args,...)
affiche une chaine (qui peut contenir des codes de format) vers stdout. De
zéro à un nombre illimité d'arguments peut être ajoutés. Notez qu'une
chaine formattée peut être crée dynamiquement, aucune vérification sur le
nombre d'arguments est (peut être) fait.
Exemple :
WriteF('salut à tous!\n') /* juste écrit un saut de ligne à */
/* la fin de la chaine */
WriteF('a = \d \n',a) /* écrit : "a = 123", si a est 123 */
Voir le morceau concernant les chaines, quelque part par ici.
NOTE : si stdout=NIL, par exemple si votre programme a été lancé du
Workbench, WriteF() ouvrira une fenêtre de sortie (uniquement sous 2.0),
et mettra le gestionnaire dans conout et stdout. Cette fenêtre sera
automatiquement refermée à la sortie du programme, après que l'utilisateur
aie frappé <return>.
WriteF() est la seule fonction qui ouvre une fenêtre, donc si vous voulez
faire des entrées/sorties sur stdout, et pour être sûr que stdout<>NIL,
faites un WriteF('') comme première instruction de votre programme par
sécurité.
Si vous voulez ouvrir une fenêtre console vous même, vous devez faire de
la sorte de placer le résultat du gestionnaire de fichier (file handle)
dans les variables stdout et conout, comme ça votre fenêtre sera fermé
automatiquement à la fin du programme.
Si vous voulez fermer vous même votre fenêtre, assurez vous de remettre
conout à NIL, pour signaler au E qu'il n'y a aucune fenêtre console à
fermer.
Out(gestionnaire,car)
char:=Inp(gestionnaire)
D'une part écrit, d'autre part lit un octet vers un fichier ou sur stdout.
Si car=-1 alors un EOF est atteint, ou une érreur s'est passé.
longueur:=FileLength(nom_chaine)
vous donne la longueur d'un fichier que vous voulez charger, et donc si
il éxiste ; ou retourne -1 pour une érreur ou si il n'est pas trouvé.
ok:=ReadStr(filehandle,estring)
Voir les fonctions de chaine
oldout:=SetStdOut(newstdout)
Fixe la variable de sortie standard stdout. C'est l'équivalent de :
oldout:=stdout ; stdout:=newstdout
9B. chaines et fonctions de chaine
----------------------------------
Le E possède le type de données STRING. C'est une chaine, appelée à partir de
maintenant chaine E ('Estring'), qui peut être modifié et changé en taille,
à opposer à la chaine normale ('string'), qui sera utilisé pour n'importe
quelle séquence terminée par un zéro.
Les chaines E sont rétroactivement compatible avec les chaines, et rien
d'autres. Alors si un argument demande une chaine normale, on y mettre
indifféremment l'une des 2 chaines. Si parcontre une chaine E est demandée,
n'utilisez pas une chaine normale.
Exemple :
DEF s[80]:STRING, n /* s est une chaine E avec une */
/* longueur maximum de 80 */
ReadStr(stdout,s) /* lit un entrée sur la console */
n:=Val(s,NIL) /* et en sort une valeur */
... etc.
Notez que toutes les fonctions de chaine sont gérer de façon à tendre vers
leur longueur maximum correctement
DEF s[5]:STRING
StrAdd(s,'ceci est une chaine de 5 caractères',ALL)
s va contenir 'ceci '.
s:=String(maxlen)
Une chaine peut donc allouée dynamiquement en mémoire système avec la
fonction String(), (note: le pointeur retourné par cette fonction doit
toujours être vérifiée.
DEF s[80]:STRING
est équivalent à :
DEF s
s:=String(10)
bool:=StrCmp(chaine1,chaine2,long)
compare 2 chaines.
long est le nombre d'octet à comparer, ou ALL pour comparer
toute la longueur.
Retourne : TRUE ou FALSE
StrCopy(chaineE,chaine,long)
copie le chaine vers la chaine E
long : nombre d'octet à copier ou ALL
StrAdd(chaineE,chaine,len)
fait comme StrCopy(), à la différence que la chaine est mis à la fin
de la chaine E (concaténation)
long:=StrLen(chaine)
calcule la longueur d'une chaine terminée par un zéro.
long:=EstrLen(chaineE)
retourne la longueur d'un chaine E.
max:=StrMax(chaineE)
retourne la longueur maximale d'un chaine E
RightStr(chaineE1,chainE2,n)
copie les n derniers caractères de la chaine 1 sur la chaine 2
MidStr(chaineE1,chaine2,pos,long)
copie un nombre de caractère (long) (tous si long=ALL) à partir de la
position pos de la chaine 2 sur la chaine 1.
NOTEZ : dans toutes les fonctions de chaines, le premier caractères à la
position 0 et non pas 1, comme c'est le cas dans des langages comme le
BASIC.
valeur:=Val(chaine,lecture)
trouve un entier dans une chaine ASCII. Les espaces/tabulations/etc...
sont sauté, et ainsi les nombres héxadécimaux (123456789abcdef) et
binaire (01) peuvent être lu de cette manière si ils sont précédés par
un '$' ou un '%' respectivement. Un '-' signifie un entier négatif.
Val() retourne le nombre de caractères lu, dans 'lecture', qui doit être
donné par référence (<-!!!). Si 'lecture' retourne 0 (et la valeur sera
aussi 0), alors la chaine ne contient pas d'entier, ou la valeur et trop
grande pour entré dans 32 bits. 'lecture' peut être NIL.
Exemples de chaines qui seront correctement analysées :
'-12345', '%10101010', ' -$ABcd12'
Ces chaines seront retournés en tant que 'valeur' et dans une variable
{lu} un zéro :
'', 'hello!'
quelle_pos:=InStr(chaine1,chaine2,pos_départ)
cherche dans la chaine 1, la chaine 2, a partir du caractère pos_départ.
L'*adresse* a laquelle lachaine 2 a été retrouvé, est retournée, sinon -1.
nouv_adr_chaine:=TrimStr(chaine)
retourne l'*adresse* du premier caractère, après avoir enlevé les espaces,
tabulations, etc...
UpperStr(chaineE)
LowerStr(chaineE)
Transforme les minuscule en masjuscule (respectivement masjuscule en
minuscule)
NOTE : ces fonctions modifient le contenu de 'chaine'. On ne peut donc
utiliser que des chaines E. Cela suppose que vous obteniez l'adresse d'une
chaine grace à une fonctions de l'Amiga. Vous devez alors faire une copie
(par StrCopy()) de cette chaine dans une chaine E, puis utiliser ces
fonctions.
ok:=ReadStr(gest_fichier,chaineE)
lit une chaine (se terminant par le code ASCII 10) de n'importe quel
fichier ou de stdout.
ok contient -1 si il y a erreur, ou une fin de fichier (EOF) si on est
à la fin.
NOTE : le contenu de la chaine lue est toujours valide.
SetStr(chainE,nouv_long)
modifie manuellement la longueur d'une chaine E. C'est pratique lorsque
qu'une fonction autre que les fonctions E, lit une chaine E, et vous
voulez continuer à utiliser cette variable.
Par exemple, après avoir utilisé une fonction qui ajoute un zero à la fin
d'un chaine E, prenez SetStr(ma_chaine,StrLen(ma_chaine)) pour manipuler
ma_chaine à nouveau.
Pour les fonctions liant les chaines, voir la chapitre 9H
9C. listes et fonctions de liste
--------------------------------
Les listes sont comme les chaines, à la différence qu'elles consistent en
LONG, et pas en CHAR.
Elles peuvent être declarées en globale, locale ou dynamiquement :
DEF maliste[100]:LIST /* local ou global */
DEF a
a:=List(10) /* dynamique */
Note que dans le dernier cas, le pointeur 'a' contient NIL
Tout comme les chaines sont représentées comme constantes dans les
expressions, les listes ont aussi cette possibilité :
[1,2,3,4]
La valeur d'une telle expression est un pointeur sur une liste initialisée.
Il en résulte que vous pouvez alors avoir une déclaration dynamique, qui sera
remplit au moment même de la déclaration :
a:=3
[1,2,a,4]
De plus, les listes peuvent avoir d'autres types que LONG par défaut :
[1,2,3]:INT
[65,66,67,0]:CHAR /* équivalent à 'ABC' */
['topaz.font',8,0,0]:textattr
OpenScreenTagList(NIL,[SA_TITLE,'Mon Ecran',TAG_DONE])
Comme le dernier exemple le montre, les listes sont très utiles dans les
fonctions système : elles sont rétroactivement compatible avec les tableaux
(ARRAY OF LONG). Les objects peuvent être utilisé n'importe où une fonction
à besoin d'un pointeur sur une structure, ou un un tableaux.
Les fonctions de listes de tags (taglist) et les arguments (vararg) peuvent
être utilisé de cette façon.
NOTE : toutes les fonctions delistes travaillent avec des listes de LONG.
Les listes typées ne sont pratiques que pour construire des structures et des
expressions.
Comme pour les chaines, une certaine hiérarchie est de rigueur :
variables de liste -> listes de constantes -> tableau de LONG/pointeur sur LONG
Quand une fonction a besoin d'un tableau de LONG, vous n'avez qu'à donner une
liste comme argument, mais qudn une fonction demande une variable de liste
(listvar) ou une liste de constante, alors un tableau de LONG ne marchera pas.
Il est important que vous compreniez la puissance des listes et en particulier
les listes typées : elles peuvent vous éviter beaucoup soucis lors du codage
d'une structure de données. Essayez d'utiliser ces listes dans vos propres
programmes, et regrdez en les fonctions dans les programmes exemples. Une fois
que vous aurez pris le coup, vous ne pourrez vous en passer !
Résumé :
[<élément>,<élément>,... ] liste immédiate (de LONGs, à utiliser
avec les fonctions de liste)
[<élément>,<élément>,... ]:<type> listes typées (pour construire des
structures)
Si <type> est un type simple comme INT ou CHAR, vous n'avez que l'équivalent
initialisé d'un tableau de <type>.
Si <type> est le nom d'un objet, vous devez construire cet objet, ou un
tableau d'<objet>, suivant la longueur de la liste.
Exemple :
[1,2,3]:INT
vous créez une structure de 6 octets, de 3 fois
16 bits pour être précis. La valeur de cette expression est un pointeur sur
cette espace mémoire. Il en va de même si vous avez un objet :
OBJECT mon_object
a:LONG, b:CHAR, c:INT
ENDOBJECT
écrire
[1,2,3]:mon_object
signifiera la création d'un structure en mémoire de 8 octets, avec les 4
premiers octets étant un LONG de valeur 1. L'octet suivant et un CHAR de
valeur 2, et les 2 derniers octets un INT (2 octets) de valeur 3. Vous pouvez
aussi écrire :
[1,2,3,4,5,6,7,8,9]:mon_object
vous créez un tableau de <mon_objet> de taille 3. Notez que de telles listes
n'ont pas besoin d'être complêtes (3,6,9... éléments), vous pouvez créer
partiellement des objets avec des listes de n'importe quelle taille.
Une dernière note sur la taille des données :
Sur l'Amiga, vous remarquerez qu'une structure comme 'mon_objet' a une taille
de 8, et rempli à 16 pour avoir un mot. Il est certainement peu probable
qu'un compilateur E pour une architecture 80x86 n'utilisera pas ce
remplissage et en fera une structure de 7 octets, et qu'un compilateur pour
une architecture sun-sparc (si je ne me trompe pas) essayera de remplire à 32
bits, qui fera une structure de 10 ou 12 octets. Certains microprocesseurs
(rares, pas existants) utilise même les nombres de bits 38:18:9 pour leur type
LONG:INT:CHAR, au lieu de 32:16:8, comme on le fait. Alors, ne concluez pas
trop vite en ce qui concerne les objets et les listes, si vous voulez que
votre code soit portable au maximum, ou n'aie d'effets secondaires.
ListCopy(listevar,liste,n)
copie n éléments de la <liste> sur la <listevar>.
Exemple :
DEF maliste[10]:LIST
ListCopy(maliste,[1,2,3,4,5],ALL)
ListAdd(listvar,liste,n)
copie n éléments de la <liste> à la fin de <listevar>.
ListCmp(list1,list2,n)
compare les 2 listes, ou n éléments d'elles.
long:=ListLen(liste)
retourne la longueur de la <liste>.
Exemple :
ListLen([a,b,c]) retourne 3
max:=ListMax(listevar)
retourne la longueur maximale possible de <listevar>
valeur:=ListItem(liste,index)
est l'équivalent de : valeur:=liste[index]
à la différence que <liste> doit être une constante au lieu d'être un
pointeur. C'est treès utile dans des cas où vous voulez directement
utiliser une liste de valeurs :
WriteF(ListItem(['ok!','pas de mem!','pas de fichier!'],erreur))
Cet exemple écrit un message d'erreur en fonction d'<erreur>.
C'est l'équivalent de :
DEF dummy:PTR TO LONG
dummy:=['ok!','pas de mem!','pas de fichier!']
WriteF(dummy[error])
SetList(listevar,nouv_long)
fixe manuellement la longueur d'une liste. C'est seulement utile lorsque
vous lisez utiliser des listes par des fonctions autres que les fonctions
spécifiques aux listes, et aue vous voulez continuer à l'utiliser comme
vraie liste.
Pour les fonstion de liste qui utilise des expressions cotées '', voir le
chapitre 11C.
Pour les fonctions liant les listes, voir le chapitre 9h.
9D. fonctions de support Intuition
----------------------------------
wptr:=OpenW(x,y,largeur,hauteur,IDCMP,wdrapeaux,titre,écran,sdrapeaux,gadlist)
crée une fenêtre où :
- wdrapeaux (wflags) sont des drapeaux pour la mise en forme de la fenêtre
comme BACKDROP, SIMPLEREFRESH... engénéral $f
- sdrapeaux (sflags) est le type d'écran où doit s'ouvrir la fenêtre
1 = workbench, 15 = particulier (custom)
- screen est un pointeur sur l'écran qui doit être valide si sdrapeux = 15
sinon screen=NIL (0)
- gadlist pointe sur une structure de liste de gadgets, que vous pouvez
facilement créer avec la fonction Gadget(), sinon NIL.
CloseW(wptr)
ferme la fenêtre de nouveau. La seule différence avec CloseWindow()
(fonction système) est qu'elle accepte le pointeur null (NIL) et remet
stdrast à zéro (NIL).
- wptr est le pointeur donné par OpenW() ou OpenWindow() pour cette
fenêtre.
sptr:=OpenS(largeur,hauteur,profondeur,sdrapeaux,titre)
ouvre un écran pour vous.
- profondeur est le nombre de plan de bit (bitplanes) (1 à 6, 1 à 8 pour
les machines AGA),
- sdrapeaux est le type de résolutions, 0 pour basse résolution, $8000
pour haute résolution, ajoutez 4 pour l'entrelacement
CloseS(sptr)
ferme l'écran. Fonctionne de même facon que CloseW().
- wptr est le pointeur donné par OpenS() ou OpenScreen() pour cet écran.
tampon_suivant:=Gadget(tampon,gadlist,id,drapeaux,x,y,largeur,chaine)
Cette fonction crée une liste de gadgets, qui peuvent être mis dans votre
fenêtre en les donnant comme argument à OpenW(), ou après l'ouverture
avec une fonctions Intiotin comme AddGlist().
- tampon (buffer) est souvent un tableaux de taille GADGETSIZE octets
pour sauver toutes les structures associées à un gadget,
- id est le n'importe quelle nombre vous permettnt de vous rappelez de
quel gadget il s'agit lorsqu'il est pressé,
- drapeaux : 0 = gadget normal
1 = gadget booléen (interrupteur)
3 = gadget booléen pressé
- largeur est la largeur en pixel. Il doit être suffisament grand pour
contenir la chaine qui est auto-centrée,
- gadlist doit être NIL pour le premier gadget, puis glistvar pour le
tampon suivant, comme ça le E poura faire le lien entre les gadgets.
La fonction retourne un pointeur sur le tampon suivant
(=tampon+GADGETSIZE).
Exemple pour 3 gadgets :
CONST MAXGADGETS=GADGETSIZE*3
DEF tampon[MAXGADGETS]:ARRAY, suivant, wptr
suivant:=Gadget(tampon,NIL,1,0,10,20,80,'bla') /* le premier gadget */
suivant:=Gadget(suivant,tampon,... )
suivant:=Gadget(suivant,tampon,... ) /* n'importe lequel */
/* lie les 2 premiers */
wptr:=OpenW( ...,buf)
Regardez les exemples comme SuperVisor.e pour une exemple parlant.
code:=Mouse()
donne l'état actuel des 2 ou 3 boutons de la souris ;
1 = gauche, 2 = droit, 4 = centre
Si par exemple code=3 alors les boutons gauches est droit sont pressés.
NOTE : ce n'est pas une réelle fonction Intuition. Si vous voulez connaitre
les évènements de la souris de manière propre, regardez dans la structure
intuimessage que votre fenêtre recoit.
C'est la seule fonction du E qui contrôle le hardware, et n'est utile aue
dans des programmes de style démos.
x:=MouseX(fenêtre)
y:=MouseY(fenêtre)
vous permet de lire les coordonées de la souris.
- fenêtre est le pointeur sur la fenêtre par rapport a laquelle les
coordonées seront relatifs
classe:=WaitIMessage(fenêtre)
Cette fonction rend plus facile l'attente d'un évènement dans une fenêtre.
Elle attend simplement qu'un intuimessage arrive, et retourne la classe
de cet évènement. Elle mets les autres variables comme le code comme
variables globales privées, afin d'y accéder grace aux fonctions
ci-dessous.
WaitImessages est l'équivalent du code suivant :
PROC waitimessage(win:PTR TO window)
DEF port,mes:PTR TO intuimessage,class,code,qual,iaddr
port:=win.userport
IF (mes:=GetMsg(port))=NIL
REPEAT
WaitPort(port)
UNTIL (mes:=GetMsg(port))<>NIL
ENDIF
class:=mes.class
code:=mes.code /* sauvé en interne */
qual:=mes.qualifier
iaddr:=mes.iaddress
ReplyMsg(mes)
ENDPROC class
comme vous le voyez, la fonction prend exactement un message, et n'oubliez
pas qu'il peut arriver plusieurs messages dans un évènement, si appelé
plus d'une fois.
Par exemple, ovus ouvrez une fenêtre qui affiche quelque chose et attend
que l'on presse sur le gqdget de fermeture de l'écran (vous avez spécifiez
IDCMP_CLOSEWINDOW) à l'ouverture de la fenêtre) :
WaitIMessage(ma_fenêtre)
ou vous avez un programme qui attend plusieurs type d'évènements, les gére
dans une boucle, et sort avec le gadget de fermeture :
or, you have a program that waits for more types of events, handles
them in a loop, and ends on a closewindow event:
WHILE (class:=WaitIMessage(win))<>IDCMP_CLOSEWINDOW
/* gestion de classes */
ENDWHILE
code:=MsgCode()
qual:=MsgQualifier()
iaddr:=MsgIaddr()
Ces fonctions vous donneront les variables globales privées mentionnées
plus haut. Les valeurs retournnées sont définies d'près l'appel le plus
récent de WaitIntuiMessage().
Exemple :
IF class:=IDCMP_GADGETUP
mon_gadget:=MsgIaddr()
IF mon_gadget.userdata=1 THEN /* ... on a pressé le gadget #1 */
ENDIF
9E. fonctions de support graphique
----------------------------------
Toutes les fonctions de support graphique qui n'implique pas directement
un rastport, utilise la variable système 'stdrast'. Elle est automatiquement
définie par le dernier appel de OpenW ou OpenS(), et remis à NIL avec CloseW()
et CloseS.
Appeler ces fonctions avec stdrast égal à NIL est légal. stdrast peut être
manuellement modifié par SetStdRast() ou stdrast:=mon_rastport.
Plot(x,y,couleur)
dessine un point sur l'écran/fenêtre dans la couleur voulue.
- couleur = 0 à 31, 0 à 255 pour les machines AGA.
Line(x1,y1,x2,y2,colour)
trace une ligne
Box(x1,y1,x2,y2,colour)
trace une boite
Colour(avant,fond)
fixe les couleurs des fonctions graphiques qui ne prennent pas de couleur
comme argument. C'est le *registre* de couleur qui est demqndé, pas la
*valeur*.
NOTE : les fonctions qui ont un argument de 'couleur', modifie le Apen du
stdrast.
TextF(x,y,chaine_formattée,args,...)
tout comme la fonction WriteF(), mais uniquement à (x,y) sur votre
stdrast, au lieu de stdout.
Voir WriteF() et les chaines
anc_rast:=SetStdRast(nouv_rast)
chnge le rastport de sortie des fonctions graphiques du E
SetTopaz(taille)
fixe la police de caractères (font) du rastport 'stdrast'. Assurez vous
que certaine police système de l'utilisateur ne modifieront pas votre
mise en page.
- taille = 8 ou 9
9F. fonctions de support du système
-----------------------------------
bool:=KickVersion(vers)
retourne TRUE si le Kickstart de la machine est égal ou supérieur à
<vers>, sinon FALSE
mem:=New(n)
créé dynamiquement un tableau (ou un espace mémoire, si vous voulez) de
<n> octets. La différence avec AllocMem() est que l'appel se fait
automatiquement avec un dreapeau de $10000 et que vous n'avez pas à
appeler Dispose(), car lié à une liste mémoire qui est automatiquement
désallouée à la sortie du programme.
Dispose(mem)
gèle n'importe quelle <mem> alloué par New(). Vous ne devez utiliser cette
fonction que si vous voulez libérer explicitement de la mémoire _pendant_
que la programme tourne, tout comme cela se fait à la sortie de celui-ci.
CleanUp(valeur_retournée)
sort du programme à n'importe quel moment. Il remplace la fonction DOS
Exit() : n'utilisez jamais celui-là ! plutôt CleanUp(), qui permet de
désallouer la mémoire, de fermer correctement les bibliothèques, etc.
La <valeur_retournée> est donnée au DOS comme code de retour (returncode).
espace:=FreeStack()
retourne l'espace libre de la pile. Cela doit toujours être 1000 ou plus.
Voir le chapitre 'implémentation' sur comment le E organise sa pile.
Si vous n'avez pas de trop grosses récursions, vous n'avez pas à vous en
faire quant à cet espace libre.
bool:=CtrlC()
retourne TRUE si Ctrl-C est pressé depuis la dernière fois, sinon FALSE.
Cela ne marche que pour les programmes lancés via le shell (des programmes
CLI).
Exemple montrant comment les 3 dernière fonctions peuvent être utilisées :
/* calcule le factoriel de l'argument de la ligne de commande */
OPT STACK=100000
PROC main()
DEF num,r
num:=Val(arg,{r})
IF r=0
WriteF('mauvais args.\n')
ELSE
WriteF('résultat: \d\n',fac(num))
ENDPROC
ENDPROC
PROC fac(n)
DEF r
IF FreeStack()<1000 OR CtrlC() THEN CleanUp(5) /* vérif suppl */
IF n=1 THEN r:=1 ELSE r:=fac(n-1)*n
ENDPROC r
Bien sûr, cette récursion marchera difficilement avec peu de pile, et quand il
marche, il est stoppé si vite par FreeStack() que vous n'aurez pas le temps
de presser Ctrl-C, mais l'idée est là.
Une définition de fac(n) comme suit est moins sûre :
PROC fac(n) RETURN IF n=1 THEN 1 ELSE fac(n-1)*n
9G. mathématiques et autres fonctions
-------------------------------------
a:=And(b,c)
a:=Or(b,c)
a:=Not(b)
a:=Eor(b,c)
Ceux-ci marche comme les opérations usuelles, aussi bien booléennes
qu'arithmétiques.
Notez que pour And() et Or(), un opérateur existe.
a:=Mul(b,c)
a:=Div(a,b)
réalise les mêmes opérations que les opérateurs '*' et '/', mais
maintenant en vrai 32 bits. Pour des raisons de rapidité, les opérations
normale sont faites en 16 bits * 16 bits = 32 bits et 32 bits / 16 bits
= 16 bits. C'est suffisent pour quasiment tous les calculs, et quand ce
n'est pas le cas, utilisez Mul() et Div().
NOTE : pour Div(), a est divisé par b, pas l'inverse.
bool:=Odd(x)
bool:=Even(x)
retourne TRUE ou FALSE si x est impaire (Odd) ou paire (Even)
num:=Rnd(max)
seed:=RndQ(seuil)
Rnd() donne un nombre au 'hasard' a partir d'un seuil allant de 0 à max-1.
Par exemple, Rnd(1000) retourne un entier entre 0 et 999.
Pour initialiser le seuil interne, appelez Rnd() avec un nombre négatif.
la valeur absolue de cette valeur sera le seuil initial.
RndQ() calcul un nombre au 'hasard' plus vite (Quicker) que Rnd(), mais
retourne un nombre sur 32 bits. Utilisez le résultats comme seuil pour le
prochain appel, et pour seuil de départ, utilisez un grande valeur, comme
$A6F87EC1.
valeur_abs:=Abs(valeur)
calcule la valeur absolue.
a:=Mod(b,c)
divise b (32bits) par c(16bits) et retourne le modulo a (16bits)
x:=Shl(y,n)
x:=Shr(y,n)
déplace y de n bits vers la gauche (Shl) ou vers la droite (Shr)
a:=Long(adr)
a:=Int(adr)
a:=Char(adr)
lit (peek) la mémoire à l'adresse <adr>, et retourne la valeur trouvée.
Ces fonctions travaille avec respectivement des valeurs de 32, 16 et 8
bits. Notez que le compilateur ne vérifie pas si l'<adr> est valide. Ces
fonctions sont implementées en E pour les cas où écrire ou lire la mémoire
par des pointeur (PTR TO <type>) demanderait une progammation complexe,
voire moins bonne.
Vous n'êtes pas encouragé à utiliser ces fonctions.
PutLong(adr,a)
PutInt(adr,a)
PutChar(adr,a)
écrit (poke) la valeur <a> à l'adresse <adr> en mémoire.
Voir : Long()
9H. fonctions liants les chaines, les listes
--------------------------------------------
Le E propose un ensemble de fonctions qui permettent la création de listes
liées (linked) avec les types de données STRING et LIST, ou chaines et
listes qui sont créés avec String() et List(). Comme vous devez le savoir
maintenant, les chaines et les listes sont des pointeurs sur leurs données
respectives, et ont des champs suppléméntaires d'offset négatif décrivant
leur longueur actuelle et leur longueur maximale. Ces offsets dont PRIVÉs.
Ajouté à ces 2 champs, n'importe quel type de donnée complexe, a un champ
'next' (suivant), égal à NIL par défaut, qui peut servir à construire une
liste de chaines liées (linked), par exemple.
Dans la suite, j'utiliserai le terme 'complexe' pour signifier un pointeur
sur une chaine ou une liste, et 'queue' (tail) pour signifier un tel pointeur
ou un qui a déja d'autres chaines liées à lui. 'queue' peut alors être un
pointeur NIL, ce qui signifie la fin de la liste.
complexe:=Link(complexe,queue)
mets la valeur <queue> dans le prochain champ de <complexe>. Retourne de
nouveau <complexe>.
Exemple :
DEF s[10]:STRING, t[10]:STRING
Link(s,t)
crée une liste liée comme : s --> t --> NIL
queue:=Next(complexe)
lit le champ suivant de la variable <complexe>. Cela peut être bien sûr
NIL, ou une liste liées entière. Appeler Next(NIL) donnera NIL, il est
donc prudent d'appeler Next() si vous n'êtes pas sûr que vous êtes à la
fin de la liste liée.
queue:=Forward(complexe,n)
pareil que Next(), seulement avance de n liens, au lieu d'un seul, donc :
Next(c) = Forward(c,1)
Vous pouvez sans crainte appeler Forward() avec n trop grand, la fonction
s'arrètera si elle rencontre NIL pendant la recherche de liens, et
retournera NIL.
DisposeLink(complexe)
fait de même que Dispose(), avec 2 différences : la fonction ne marche
qu'avec les chaines et listes allouées par String() et List(), et
désallouera automatiquementla queue du complexe aussi.
Notez que de grandes listes liées contenant des chaines alouées par
String(), tout comme celles allouée localement ou globalement avec
STRING, doivent être désallouée de cette façon.
Pour un bon exemple de comment des listes liées de chaines peuvent être
utilisé, voir le programme 'D.e'
+---------------------------------------------------------------+
| 10. FONCTIONS DE BIBLIOTHEQUES ET MODULES |
+---------------------------------------------------------------+
10A. appels de bibliothèque prédéfinis (built-in)
-------------------------------------------------
Comme vous l'avez remarqué dans les sections précédentes, le morceau de code
lié (linked) automatiquement au début de votre source, appelé 'code
d'initialisation', ouvre toujours les 4 bibliothèques INtuition, Dos, Graphics,
Mathffp, et à cause de ça, le compilateur possède tous les appels des 5
bibliothèques (avec l'exec) (il y en a une petite centaine).
Ces appels sont valables jusqu'au workbench 2.04. Ceux du système 3.0 devrait
être inclus dans la prochaine version d'Amiga E.
Exemple, pour appeler la fonction Open(), de la bibliothèque Dos, écrivez :
gestion:=Open('mon_fichier',OLDFILE)
ou AddDisplayInfo() de la bibliothèques graphics :
AddDisplayInfo(mes_info)
Aussi simple que ça !
10B. interfacer le système Amiga avec les modules 2.04
------------------------------------------------------
Pour utiliser d'autres bibliothèques que les 5 décrit dans la section
précédente, vous avez besoin de modules. Aussi, si vous voulez utiliser des
objet (OBJECT) ou des constantes (CONST) d'includes utilisé en C ou en
Assembleur, vous vew besoin de modules.
Les modules sont des fichiers binaires qui peuvent inclure des définitions de
constantes, d'objets, de bibliothèques et de fonction. L'avantage que les
modules soient compilées, par rapport à des fichiers ASCII, est qu'ils n'ont
pas besoin d'être recompilé encore et encore, chaque fois que votre programme
est compilé. Le désavantage est qu'ils ne peuvent pas être facilement être
visualisés ; ils ont besoin d'utilitaires comme ShowModule (voir
utilitaire.doc) pour les rendre visible.
Les modules contenant les définitions de bibliothèque (les appels) sont dans
le répertoire racine de emodules: (le répertoire de module de la distribution)
Les définitions des constantes/objets sont dans les sous-répertoires,
structurés comme les modules originaux de Commodore.
syntaxe: MODULE <nom_module>,...
charge un module. Utiliser les modules vous permet d'utiliser des bibiothèques
et des fonctions alors inconnus du compilateur (et du système).
Exemple : un raccourcit du programme 'source/examples/asldemo.e' qui utilise
des modules pour afficher une boites de requête de fichier (filerequester), de
la bibliothèque Asl.library du système 2.0.
MODULE 'Asl', 'libraries/Asl'
PROC main()
DEF req:PTR TO filerequestr
IF aslbase:=OpenLibrary('asl.library',37)
IF req:=AllocFileRequest()
IF RequestFile(req) THEN WriteF('File: "\s" in "\s"\n',
req.file,req.dir)
FreeFileRequest(req)
ENDIF
CloseLibrary(aslbase)
ENDIF
ENDPROC
Du module 'asl', le compilateur prend les définitions des fonctions de l'asl
comme RequestFile(), et la variable globale 'aslbase', qui doit seulement être
initialisée par le programmeur.
Du module 'libraries/asl', il prend la définition de l'objet de la boite de
requête de fichier (filerequester), qu'on utilise pour lire le fichier choisit
par l'utilisateur.
Alors, ce n'était pas trop dur de programmer une boite de requête en E ?
+---------------------------------------------------------------+
| 11. EXPRESSIONS COTÉES |
+---------------------------------------------------------------+
11A. les cotes (quotes) et analyse (scope)
------------------------------------------
Les expressions cotées commence avec une apostrophe inverse `. La valeur
d'une expressions cotées n'est pas le résultats d'un calcul d'une expression,
mais l'adresse du code. Le résultat peut être passé à une variable normale, ou
comme argument de certaines fonctions.
Exemple:
mafonc:=`x*x*x
mafonc est maintenant un pointeur sur une fonction qui calcule x^3 quand il
est évaluée. Ces pointeurs de fonctions sont très différent des procédures
normales et ne doivent jamais être mélangées. Les plus grandes différences
sont qu'une expression cotée est une simple expression, et donc, ne peut pas
avoir sa propre. Dans notre exemple, 'x' est juste une variable locale ou
globale. C'est là que nous devons être prudent :
Si on évalue mafonc plus tard dans la même procédure, 'x' doit être local,
mais si mafonc est donné comme paramètre à une autre procédure, et puis
évauluée, 'x' doit être global. Il n'y a aucune verification du compilateur.
11B. Eval(fonc)
---------------
évalue simplement une expressions cotées (exp = Eval(`exp)).
NOTE : parce que le E est un langage peu typé, écrire accidentellement
'Eval(x*x)' au lieu de 'Eval(`x*x) ne sera pas remarqué par le compilateur,
et vous donnera beaucoup de problême : la valeur de x*x sera utilié comme
pointeur à coder.
Pour comprendre pourquoi les 'expressions cotées' sont un atout puissant,
pensez au cas suivant :
- if vous voulez réalisqer un ensemble d'actions suisvant un ensemble de
variables, vous devez normalement écrire une fonction, et l'appeler avec
différents arguments. Mais que ce passe-t-il si l'élément que vous voulez
passer tout un code ? Dans les langages traditionnels, c'est impossible.
Pas en E.
Exemple : supposons quez vous voulez écrire qui comptele temps de différentes
expressions. En E, on écrire :
PROC timing(fonc,titre)
/* faites plein de chose pour définir le temps */
Eval(fonc)
/* et le reste */
WriteF('temps mesuré pour \s est \d\n',titre,t)
ENDPROC
et appelez le avec :
timing(`x*x*x,'multiplication')
timing(`sizycalc(),'gros calcul')
dans d'autres langages, vous devez avoir des copies de 'timing()' pour
chaque appel, ou vous devez mettre chaque expression dans une fonction
séparé.
Un autre exemple simple : pensez à ce que vous pouvez faire avec des
structures de données (listes) rempli de code non évalué :
tracefonc:=[`Plot(x,y,c),`Line(x,y,x+10,y+10,c),`Box(x,y,x+20,y+20,c)]
Notez que l'idée de fonctions comme des variables/valeurs normales n'est pas
nouvelles en E. Les expressions cotées viennent literalement du LISP, qui
possède la fonction encore plus puissante appelé Lambda, qui peut être donnée
comme arguments à des fonctions ; les expressions cotées de E peuvent aussi
être vu comme des fonction Lambda sans paramêtre (ou seulement avec des
paramêtres globals).
11C. built-in functions
----------------------
MapList(varadr,liste,listevar,fonc)
calcule la fonction sur tous les éléments de <liste>, et retourne tous les
résultats dans <listevar>. <func> doit être une expressions cotées (voir
plus haut). <varadr> est donné par référence.
Exemple :
MapList({x},[1,2,3,4,5],r,`x*x) r est alors : [1,4,9,16,25]
ForAll(varadr,list,func)
retourne TRUE si tous les éléments de la liste sont évalués par la
fonction comme vrais, sinon FAUX. Peut être aussi utilisé pour réaliser
une certaine fonction sur tous les éléments d'une liste :
ForAll({x},['un','deux','trois'],`WriteF('exemple: \s\n',x))
Exists(varadr,list,func)
Comme pour ForAll(), mais retourne TRUE si la fonction évalue au moins
un élément de vrai (<>0).
Notez que ForAll() évalue toujours tous les éléments, mais pas forcément
Exist().
Exemple de comment utiliser ces fonctions en pratique :
on alloue differentes tailles en mémoire en une définition, les vérifie en
une seule fois, et les libère toutes, mais uniquement celles qui ont été
alouée. (v37+) :
PROC main()
LOCAL mem[4]:LIST,x
MapList({x},[200,80,10,2500],mem,`AllocVec(x,0)) /* alloue quelques */
IF ForAll({x},mem,`x) /* suxxes ? */
WriteF('Yes!\n')
ELSE
WriteF('No!\n')
ENDIF
ForAll({x},mem,`IF x THEN FreeVec(x) ELSE NOP) /* libère seulement */
/* ceux qui sont <>NIL */
ENDPROC
Notez l'abscence d'itération dans ce code. Essayez de reécrire cet exemple
dans autre langage pour c'est si spécial.
+---------------------------------------------------------------+
| 12. SUPPORT DES NOMBRES FLOTTANTS |
+---------------------------------------------------------------+
12A. utiliser les flottants et les opérateurs flottants
-------------------------------------------------------
Surpasser les opérateurs standard + * etc, avec leurs équivalents flottants
est possible à partir du système 2.0 de l'Amiga E, maisj'ai enlevé la partie
principale de la documentation les concernant, car le concept flottant en E
changera avec les versions supérieures : ces versions vous permettra du code
inline pour 68881 pour des routines FFP, et de façon transparente.
Si vous voulez réellement utiliser les flottants avec la version 2.1b, vous
êtes prié d'utiliser les routines prédéfinies SpXxx() de la bibliothèque
mathffp.library.
Exemple:
x:=SpMul(y,0.013483)
Attention quand la prochaine version sortira, votre source devra être modifié
(en mieux !).
12B. expressions flottantes et conversion
-----------------------------------------
Comme 12A.
+---------------------------------------------------------------+
| 13. GESTION DES ERREURS |
+---------------------------------------------------------------+
13A. définition des gestion d'erreurs (HANDLE/EXCEPT)
-----------------------------------------------------
Le mécanisme d'exception est a peu près le même qu'en ADA ; il permet des
réactions flexibles sur les erreurs de votre programme et les opérations
de resources complexes.
NOTE : le terme d'exception' en E n'a que peut de relation avec les
exceptions venant des processeurs 680x0.
Une gestion d'erreur se fait par un bout de code qui sera appelé quand une
erreur survient lors du fonctionnement du programme, comme une fenêtre qui
ne s'ouvre pas, ou de la mémoire non allouée. Vous, ou le système lui-même,
dira que quelque chose ne va pas (cela s'appelle 'raising an exception' -
'lever une exception'), alors le système cherchera le bon gestionnaire que
vous aurez programmé. Je dit 'le bon' car le programme peux avoir plusieurs
gestionnaire, a chaque niveau du programme.
Une définition de fonction normale doit (comme on le sait) ressembler à ça :
PROC bla()
/* ... */
ENDPROC
Une fonction avec un gestionnaire d'exception, à ça :
PROC bla() HANDLE
/* ... */
EXCEPT
/* ... */
ENDPROC
Le bloc entre PROC et EXCEPT est éxécuter normalement, et si aucune erreur
n'arrive, le bloc ntre EXCEPT et ENDPROC est sauté. La procédure se termine
au ENDPROC. Si une exception est levée, soit dans les partie du PROC, ou
dans n'importe quelle fonction appelée dans ce bloc, le gestionnaire
d'exception est invoqué.
13B. utiliser la fontion Raise()
--------------------------------
Il y a beaucoup de moyens de lever une exception, la plus simple est
d'utiliser la fonction Raise() :
Raise(exceptionID)
<exceptionID> est simplement une constante qui définie le type d'exception,
et est utilisé par les gestionnaires pour définir ce qui n'allait pas.
Exemple :
ENUM NOMEM,NOFILE /* et d'autres */
PROC bla() HANDLE
DEF mem
IF (mem:=New(10))=NIL THEN Raise(NOMEM)
ma_fonc()
EXCEPT
SELECT exception
CASE NOMEM
WriteF('Pas de mémoire!\n')
/* ... et d'autres */
ENDSELECT
ENDPROC
PROC mafonc()
DEF mem
IF (mem:=New(10))=NIL THEN Raise(NOMEM)
ENDPROC
La variable 'exception' dans le gestionnaire contient toujours la valeur
de l'argument passé par la fonction Raise().
Dans les 2 cas du New(), la fonction Raise() appel le gestionnaire de la
fonction bla(), et retourne à la fonction qui a appelé.
Si ma_fonc() avait son propre gestionnaire d'exception, c'est celui là qui
serait appelé par le New() de la fonction ma_fonc(). La recherche d'un
gestionnaire se fait dès le début de la procédure où il est définit
jusqu'au EXCEPT, en incluant tous les appels fait a partir de là.
Cela a 3 conséquences :
A. les gestionnaires sont organisés de façon récursive. Le gestionnaire
actuellement appelé est dépendant de la fonction appelé durant le
fonctionnement du programme.
B. Si une exception est levée au sein d'un gestionnaire, le gestionnaire du
niveau inférieur (le précédent) est appelé. Cette caractèristique peut
être utilisé pour construire des schémas d'allocation de resources
récursives complexes avec une grande facilité, comme on l'a vu rapidement.
C. Si une exception est levée à un niveau où aucun autre gestionnaire de
niveau inférieur existe (ou dans un programme qui n'a pas du tout de
gestionnaire du tout), le programme est arrté (c'est à dire qu'un Raise(x)
à le même effet qu'un CLeanUp(0)).
13C. definition des exceptions pour les fonctions prédéfinies (RAISE/IF)
------------------------------------------------------------------------
Avec les exceptions précédentes, nous nous sommes épargné le gros travail de
ocnstruction de notre propre fonction d'erreur. Mais il faut toujours faire
des vérification à chaque appel de New().
Le système de gestion des exceptions du E permet la défintion d'exceptions
pour toutes les fonctions du E (comme New(), OPenW, etc.) et pour toutes les
fonction sur les bibliothèques (OpenLibrary(), AllocMem(), etc.) et même
celles inclues dans les moidules.
Syntaxe : RAISE <exceptionId> IF <fonc> <comp> <valeur> , ...
La partie après RAISE peut être répétée, et séparé par une virgule ','.
Exemple:
RAISE NOMEM IF New()=NIL,
NOLIBRARY IF OpenLibrary()=NIL
La première ligne dit quelque chose comme : 'chaque fois qu'un appel de New()
retourne NIL, alors automatiquement, lève l'exception NOMEM'.
<comp> est l'un de ces symboles = <> > < >= <=
Apreès cette défnition, vous pouvez écrire votre programme :
mem:=New(taile)
sans avoir à écrire :
IF mem=NIL THEN Raise(NOMEM)
Notez que la seule différence est que <mem> ne recoit alors aucune valeur
si le gestionnaire est appelé : le code est généré pour tous les appels de
New(), vérifie directement après la fin de l'éxécution de New() et appelle
Raise() si névessaire.
Nous allons implémenter un petit exemple qui serait difficile à résoudre
sans gestion d'exceptions : on appel une fonction récursivement, et dans
chacune on alloue une resource (dans notre cas, de la mémoire), que l'on
alloue avant, et que l'on libère après l'appel récursif. Que se passe-t-il
si une erreur survient quelque part loin dans la récursion ; doit-on quitter
le programme? Oui : dans un langage conventionnel, on ne pourrait pas libérer
les resources plus bas dans la récursion en sortant du programme, car tous les
pointeurs sur cette mémoire sont sauvés dans des variables locales impossibles
à atteindre. En E, on n'a seulement qu'à lever un exception, appelant
récursivement tous les gestionnaires et libérant toutes les resources.
Exemple :
CONST SIZE=100000
ENUM NOMEM /* ,... */
RAISE NOMEM IF AllocMem()=NIL
PROC main()
alloc()
ENDPROC
PROC alloc() HANDLE
DEF mem
mem:=AllocMem(SIZE,0) /* regarde combien de bloc peuvent */
/* être alloué */
alloc() /* fait la récursion */
FreeMem(mem,SIZE) /* on n'ira jamais jusqu'ici */
EXCEPT
IF mem THEN FreeMem(mem,SIZE)
Raise(exception) /* appelle récursivement tous les */
/* gestionnaires */
ENDPROC
C'est bien sûr une simulation d'un problême de programmation courant qui est
beaucoup plus complexe, et la nécessité d'une gestion d'exceptions devient
évidente. Pour un exemple concret de programme où une gestion d'erreur serait
serait très difficile sans gestion d'exception, voir l'utilitaire 'D.e'.
13D. utilisation des identificateurs d'exceptions (ID)
------------------------------------------------------
En réalité, un identificateur d'exceptions est bien sûr une valeur de 32
bits, et vous puvez passer pratiquement n'importe quoi à un gestionnaire
d'exception : par exemple, certain l'utilise pour passer des descriptions
d'erreur.
Raise('Ne peut pas ouvrir la "gadtools.library"!')
En tout cas, si vous voulez bien utiliser les exceptions, et voulez avoir la
posiibilité d'utilise de futur module qui lève des exceptions non définies
par votre programme, suivez les conseils suivants :
- Utilisez et definissez l'ID 0 comme 'pas d'erreur'
- Pour des exceptions particulières, utilisez des IDs de 1 à 10000.
Définissez les avec ENUM :
- For exceptions specific to your program, use the ID's 1-10000.
ENUM OK,NOMEM,NOFILE,...
(OK est égale à 0, et les suivant 1+)
- Les IDs de 12336 à 2054847098 sont réservés pour des exceptions courantes.
(Ces identificateurs consistent en des lettres majuscule/minuscule de
longueur 2, 3 ou 4, encadrés par "").
Une exception courantes est une exception qui n'a pas besoin d'être définie
dans votre programme, et qui peuvent être utiliser par des modules
(incorporants aussi des fonctions) pour lever un exception :
Par exemple, vous contruisez un ensemble de procédures qui réalisent une
certaine tâche, et voulez lever des exceptions. Comme vous voulez utiliser
ces fonctions dans plusieurs programmes, vous aurez du mal à coordonner les
IDs avec le programme principal, d'autant plus dure si vous utilisez
plusieurs ensemble de procédures, qui utilisent différents IDs pour la même
erreur !
C'est qu'interviennent les exceptions courantes : l'ID commun pour 'plus de
mémoire' (out of memory) est "MEM" (avec le guillemets) : chaque
implementation n'a qu'a appeler
Raise("MEM")
de n'importe quelle procedure, et le programmeur qui utilise le module
n'a besoin que de gérer un exception qui reconnait "MEM".
Les futurs modules qui contiendront des ensembles de fonctions spécifieront
quelle exception est levée par telle procédure, et si elle prend le dessus
sur un autre ID d' autres procédures. La tâche du programmeur qui à affaire
aux exceptions est grandement simplifiée.
Exemples:
(système)
"MEM" plus de mémoire
"FLOW" (proche du) dépassement de la pile
"^C" arrêt par Control-C
"ARGS" mauvais arguments
(exec/libraries)
"SIG" ne peut allouer le signal
"PORT" ne peut créer de messageport
"LIB" bibliothèque non accessible
"ASL" pas d' asl.library
"UTIL" pas d' utility.library
"LOC" pas de locale.library
"REQ" pas de req.library
"RT" pas de reqtools.library
"GT" pas de gadtools.library (pareil pour les autres)
(intuition/gadtools/asl)
"WIN" impossible d'ouvrir une fenêtre
"SCR" impossible d'ouvrir un écran
"REQ" ne peut pas ouvrir une boite de requête (requester)
"FREQ" ne peut pas ouvrir une boite de requete de fichier
(filerequester)
"GAD" ne peut pas créer de gadget
"MENU" ne peut pas créer de menu(s)
(dos)
"OPEN" ne peut pas ouvrir le fichier / le fichier n'éxiste pas
"OUT" problême de lecture
"IN" problême d'écriture
"EOF" fin de fichier (end of file) mal placé
"FORM" mauvais format d'entrée (input format)
La tendance générale est aux majuscules pour les exceptions système général
et minuscule (et melange) pour les modules spécifiques.
Tous les autres IDs (avec les IDs négatifs) sont réservés.
+---------------------------------------------------------------+
| 14. PROGRAMMATION ORIENTEE OBJECT (OO) |
+---------------------------------------------------------------+
Comme elle n'a pas encore été iplémentée, elle n'est pas n'ont plus
documentée.
+---------------------------------------------------------------+
| 15. ASSEMBLEUR INLINE |
+---------------------------------------------------------------+
15A. utilisation des identificateurs
------------------------------------
Comme vous l'aurez préssenti dans l'exemple du chapitre 5D, les instructions
assembleurs peuvent librement mélangé avec le code E. Le grand secret est
qu'un assembleur complet est construit dans le compilateur.
En plus des modes d'adressage assembleur normaux, vous pouvez utiliser les
identificateurs suivant :
mylabel:
LEA mylabel(PC),A1 /* labels */
DEF a /* variables */
MOVE.L (A0)+,a /* notez que <var> est un <offset>(A4) */
/* (or A5) */
MOVE.L dosbase,A6 /* identificateur d'appel */
/* d'un bibliothèque */
JSR Output(A6)
MOVEQ #TRUE,D0 /* constantes */
15B. l'assembleur en ligne comparé à un macro assembleur
--------------------------------------------------------
L'assembleur en ligne diffère un peu d'un macro assembleur moyen, et ce à
cause du fait que c'est une extension du E, et qu'il obéit à la syntaxe du E.
Les grosses différences sont :
- les commentaires sont fait avec /* et */ et non par ';', ils ont une
significations différentes.
- les mots-clé et regirstres sont en majuscules, tout est sensible (case
sensitive).
- pas de macros ou autres (IL y a le langage E fait pour ça !)
- vous devez faire attention que les registres A4/A5 ne sont pas détruit par
un code assembleur en ligne, car utilisé par le E.
- pas _encore_ de support pour le modèle LARGE/reloc-hunks en assembleur.
Cela siginifie en pratique que vous devz utiliser un adressage relativ pour
l'instant.
15C. comment utiliser des données binaires (INCBIN/CHAR..)
----------------------------------------------------------
INCBIN
syntaxe: INCBIN <nom_fichier>
inclue un fichier binaire a ce point précis, doit être séparé du code.
Exemple:
montab: INCBIN 'df1:data/blabla.bin'
LONG, INT, CHAR
syntaxe: LONG <valeurs>,...
INT <valeurs>,...
CHAR <valeurs>,...
vous permet de placer des données binaires dans votre programme comme
DC.x en assembleur. Notez que la déclaration CHAR prend aussi les chaines,
et sera toujours mis à un mot pair.
Exemple :
mes_données: LONG 1,2; CHAR 3,4,'Salut !',0,1
15D. OPT ASM
------------
On peut trouver quelques autres détails sur OPT ASM dans le chapitre 16A.
Cette option vous permet de mettre 'EC' en mode assembleur. Il n'y a aucune
bonne raison d'utiliser EC plutôt que quelques macro-assembleurs à part le
fait qu'il est significativement plus rapide que par exemple A68k, égale
DevPac et est derrière ASmOne (snif... 8-{). Vous passerez aussi beaucoup de
temps en essayant de réduire le nombre de disquettes des bons vieux sources
du Seka via EC, à cause des différences décrite dans la chapitre 15B. Si vous
voulez écrire un programme en assembleur avec EC, et voulez rendre le source
compatible avec d'autres assembleurs, simplement faites précéder chaque
élément spécifique du E d'un ';', EC les utilisera mais les autres assembleurs
les verrons comme commentaires.
Exemple :
; OPT ASM
start: MOVEQ #1,D0 ; /* faite n'importe quoi */
RTS ; /* et sort */
Ceci sera assemblé par n'importe quel assembleur, même EC
+---------------------------------------------------------------+
| 16. REVUE D'IMPLEMENTATION |
+---------------------------------------------------------------+
16A. le mot-clé OPT
-------------------
OPT, LARGE, STACK, ASM, NOWARN, DIR, OSVERSION
syntaxe: OPT <options>,...
vous permet de changer les options par défaut du compilateur :
LARGE met le modèle de données et du code sur grand (LARGE).
Petit par défaut. Le compilateur génère un code 100%
relatif de style pc, avec un taile maximum de 32k. Avec
LARGE, il n'y a pas de limites, et reloc-hunks est généré.
Voir -l
STACK=x fixe la taille de la pile à x octets. A utiliser
uniquement si vous savez ce que vous faites. Normalement
le compilateur fait une bonne approximation de l'espace
nécessaire.
ASM met le compilateur en mode assembleur. A partir de là,
uniquement des instructions assembleur sont acceptés, et
aucun code d'initialisation n'est généré. Voir le chapitre
assembleur en ligne.
NOWARN Ne fait pas afficher les avertissements. Le compilateur
vous préviendra si il *pense* que votre programme est
incorrect, mais a une bonne syntaxe. Voir -n
DIR=moduledir fixe le répertoire dans lequel le compilateur cherchera
les modules. 'Emodules:' par défault
OSVERSION=ver =33 par défaut (v1.2). Fixe la version minimum du
kickstart (comme 37 pour v2.04)sur lequel votre programme
doit tourner. Comme ca, votre programme ne s'arrêtera lors
de l'ouverture de la dos.library si vous avez une ancienne
machine. En tout cas, vérifiez vous-même la version et
donner un message d'erreur approprié sera une aide à
l'utilisateur.
Exemple:
OPT STACK=20000,NOWARN,DIR='df1:modules',OSVERSION=39
16B. petit et grand modèle
--------------------------
L'Amiga E vous laisse le choix entre un petit (SMALL) et un grand (LARGE)
modèle. Notez que la plupart des programmes que vous écrirez (spécialement si
vous débiutez en E) rentrera dans les 32 Ko une fois compilés : vous n'aurez
pas à vous soucier du modèle du code. Vous saurez qu'il faudra utiliser le
grand modèle dès que EC commencera à dire qu'il ne peut plus réduire le code
à 32k. Pour compiler un source avec un grand modèle :
1> ec -l grand.e
ou mieux encore, mettez
OPT LARGE
dans votre code.
16C. organisation de la pile
----------------------------
Pour garder les variables locales et globales, le système d'un éxécutable
généré par Amiga E alloue un bloc de mémoire, duquel il prendra un bout pour
les variables globales. Le reste sera utilisé dynamiquement par les appels
des fonctions.
Quand une fonction est appelé en E, un espace dans la pile est reservée
pour les données locales, qui sera libéré à la sortie de la fonction.
C'est pourquoi avoir un grand tableau comme données locales peut être
dangereux si utilisé récursivement : toutes les données de lappel précédent
de la même fonction reste dans la pile, et consomme une grande partie de la
pile encore libre. Toutefois si les procédures sont correctement appelées,
il n'y a aucune raison d'avoir un débordement.
Exemple:
donnée globale : 10k (par ex. un tableau)
donnée locale PROC #1: 1k
donnée locale PORC #1: 3k
Le système reserve toujours 10k d'espace supplementaire pour les récursions
normales (par exemple avec un petit tableau local) et des espaces pour des
tampons (buffers) et le système, qui prendront en tout 24 k de pile en plus.
16D. limites du code
--------------------
Notez ces signes : (+-) = dépend du contexte
(n.l.) = pas de limite, mais semble raisonnable.
--------------------------------------------------------------------------
OBJECT/ITEM (objet/membre) SIZE/AMOUNT/MAX (taille)
--------------------------------------------------------------------------
type de donnée CHAR 0 .. 255
type de donnée INT -32 k .. +32 k
type de donnée LONG/PTR -2 gig .. +2 giga
longueur d'identificateur 100 octets (n.l.)
longueur d'un ligne de commandes 2000 tokens léxicaux(+-)
longueur d'un source 2 giga (théoriquement)
listes de constantes quelques 100aines (+-)
chaines constantes 1000 chars (n.l.)
nombre max. de boucle imbriqué (IF, FOR etc.) 500
nombre max. de commentaire imbriqué infini
# de variables locales par procédure 8000
# de variables globales 7500
# d'arguments pour vos propres fonctions 8000 (ensemble avec locales)
# d'arguments de fonction E (WriteF()) 64
un objet (alloué en local/global ou dyn.) 8 k
un tableau, liste ou chaine (local or global) 32 k
une chaine (dynamique) 32 k
une liste (dynamique) 128 k
un tableau (dynamique) 2 giga
données locales par procédure 250 méga
données globales 250 méga
taille du code d'une procédure 32 k
taille du code d'un éxécutable 32 k en petit (SMALL) modèle,
2 giga en grand (LARGE) modèle
limite courante (étendu dans la futur) 2-5 méga
taille des tampons du code généré
et des identificateurs suivant le source
taille des tampons des labels/branches et
intermédiaire (re) alloué indépendamment
16E. les messages d'erreurs, d'avertissements et de non référence
-----------------------------------------------------------------
Quelques fois, à la compilation de votre source avec EC, vous aurez un
message du style : UNREFFRENCED: <ident>, <ident>, ...
C'est le cas si vous déclarez des variables, fonctions ou labels dont vous
ne vous servez pas. C'est en service qui vous est rendu par le compilateur
pour vous aidez à trouver ces erreurs difficiles à trouver.
Il y a plusieurs avertissements que le compilateur envoie pour vous dire que
quelquechose devrait mal se passer, mais n'est pas vraiment une erreur.
- 'A4/A5 used in inline assembly' 'A4/A5 utilisé dans les lignes assembleur'
Cet avertissment apparait lorsque vous utilisez les registres A4 ou A5 dans
votre code. La raison de cela est que ces registres sont utilisé de façon
interne par le E pour adreser les variables globales et locales.
Bien sûr il y a de bonne raison de les utiliser, comme pour faire un
MOVEM.L A4/A5,-(A7) avant un grand morceau de code assembleur.
- 'keep an eye on your stacksize' 'jetez un oeil à la taile de votre pile'
- 'stack is definitely too small' 'la pile est vraiment trop petite'
Ces deux apparaitrons si vcous utilisez OPT STACK=<size>. Le compilateur
comparera votre taille à sa propre estimation (voir le chapitre 16C), et
retournera la première des erreurs si il pense que c'est bien mais un peu
juste, et la seconde si c'est bien trop petit.
- 'suspicious use of "=" in void expressions'
'mauvaise utilisation de "=" dans une expression vide'
Cet avertissment sera retourné si vous écrivez des expressions comme 'a=1'
comme déclaration. Un raison de cela est le fait qu'un comparaison n'a pas
de sens comme déclaration, mais la principale raison est que cela provient
souvent d'une faute de frappe en voulant écrire 'a:=1'. Oublier ces ':'
serait difficile à trouver, et aurait des conséquences désastreuses.
Erreurs.
- 'syntax error' 'Erreur de syntaxe'
Erreur le plus souvent rencontrée. Cette erreur survient soit
lorsqu'aucune other erreur est appriopriée, soit l'agencment de votre
code est anormal.
- 'unknown keyword/const' 'mot-clé ou constantes inconnus'
Vous avez utilisé un identificateur en majuscule (comme 'IF' ou 'TRUE'),
et le compilateur ne trouve aucune définition. Causes :
- mot-clé mal écrit,
- vous utilisez une constante, mais vous avez oublié de la définir dans
la déclaration d'un CONST,
- vous avez oublié to spécifier le module dans lequel la constante est
définie.
- '":=" expected' '":=" nécessaire'
Vous avez écrit un FOR ou un assignement, et écrit quelque chose d'autre
que ':='.
- 'unexpected characters in line' 'caractères non corrects dans la ligne'
Vous utilisez des caratères en dehors d'une chaine, et qui n'ont aucun
sens pour le E
Exemple : @, !, &, \, ~
- 'label expected' 'label nécessaire'
A certains endroit, par exemple aprés un PROC ou un JUMP, un label est
nécessaire, et vous avez écrit autre chose.
- '"," expected' '"," nécessaire'
En spécifiant une list d'éléments (par exemple une liste de paramètres)
vous avez écrit quelque chose d'autre à la place de la virgule.
- 'variable expected' 'variable nécessaire'
Cette constructio demande un variable.
Exemple :
FOR <var>:= ... etc.
- 'value does not fit in 32 bit' 'valeur ne rentre pas dans les 32 bits'
En spécifiant une constante (voir les chapitres 2A-2E), vous avez écrit
un nombre trop grand (par exemple : $FFFFFFFFF, "abcdef").
Cette erreur apparait aussi lorsque vous définissez plus de 32 éléments
dans la déclaration d'un SET.
- 'missing apostrophe/quote' 'apostrophe/cote manquante'
Vous avez oublié une ' à la fin de la chaine.
- 'incoherent program structure' 'structure incohérente du programme'
- vous avez commencer une nouvelle procédure (PROC) avant de terminer
la précédente.
- vous n'avez pas correctement inclue les boucles les unes dans les
autres.
Exemple :
FOR
IF
ENDFOR /* inversion du ENDFOR et du ENDIF */
ENDIF
- 'illegal command-line option' 'mauvaise option dans la ligne de commande'
En spécifiant 'EC -opt source' vous avez écrit les options avec quelque
chose que EC ne connait pas.
- 'division and multiplication 16bit only'
'division et multiplication seulement sur 16 bits'
Le compilateur a détecté que vous utilisez des valeurs sur 32 bits pour
* ou /. Cela ne donnera pas la valeur désiré lorsquele programme sera
lancé. Voir Mul() et Div().
- 'superfluous items in expression/statement'
'trop de membres dans l'expression/déclaration'
Après la compilation, le compilateur trouve touours des 'tokens' au lieu
d'une fin de fichier (end of line). Vous avez probablement oublié une
saut de ligne <lf> ou un ';' pour séparer 2 déclarations.
- 'procedure "main" not available' 'pas de procédure Main()'
Votre programme n'a pas de proceédure Main() !
- 'double declaration of label' 'double déclaration d'un label'
Vous avez déclaré 2 fois un label.
Exemple :
label:
PROC label()
- 'unsafe use of "*" or "/"' 'utilisation peu prudente de "*" ou "/"'
Ceci encore concerne les 16 bits au lieu des opérateur 32 bits * ey /.
Voir 'division and multiplication 16bit only'.
- "reading sourcefile didn't succeed"
'la lecture du fichier source n'a pas réussi'
Vérifiez votre source, en particulier si en donnant 'ec monsource',
assurez vous que le nom du fichier se termine par '.e' (ce qui n'est pas
le cas dansla ligne de commande)
- "writing executable didn't succeed"
'l'écriture de l'éxécutable n'a pas réussi'
En écrivant le code généré, comme éxécutable, le DOS retourne une erreur.
Par exemple, l'éxécutable déja existant ne peut être effacé et rééecrit.
- 'no args' 'pas d'arguments'
"USAGE: ec [-opts] <sourcecodefilename> (`.e' is added)"
"USAGE: ec [-opts] <nom_du_fichier_soure> (`.e' est rajouté)"
Vous aurez ceci en tapant uniquement 'ec', sans arguments.
- 'unknown/illegal addressing mode' 'mode d'adressage inconnu/illégal'
Cette erreur est retourné uniquement par les lignes assembleur. Les causes
possibles sont :
- vous avez utilisé un mode d'adressage qui n'éxiste pas sur le 68000,
- le mode d'adressage éxiste, mais pas pour cette instruction.
Toutes les instructions assembleur ne supporte pas tutes les
combinaisons d'adresses effective comme source et destination.
- 'unmatched parentheses' 'mauvais nombre de parenthèses'
Votre déclaration comporte plus de '(' que de ')' ou l'inverse.
- 'double declaration' 'double déclaration'
Un identificateur est utilisé dans 2 ou plus déclarations.
- 'unknown identifier' 'identificateur inconnu'
Un identificateur n'est utilisé dans aucune déclaration; il est inconnu.
Vous avez certzinement oublié de le mettre dans la déclaration DEF.
- 'incorrect #of args or use of ()' 'nombre incorrect d'arguments ou de ()'
- vous avez oublié de mettre une '(' ou une ')' au bon endroit,
- vous donner un nombre incorrect d'argument à un fonction.
- 'unknown e/library function' 'fonction inconnue du E/bibliothèque'
Vous avez écrit un identificateur avec le premier caractère en majuscule,
et le second en minuscule, mais le compilateur ne trouve aucune
définition. Causes possibles :
- le nom de la fonction est mal écrit,
- vous avez oublié d'inclure le module qui défini l'appel de la
bibliothèque.
- 'illegal function call' 'appel de fonction illégal'
N'arrive que rarement. Vous l'aurez si vous essayez de construire
des appels de fonction complexe, comme :
WriteF(WriteF('hi!'))
- 'unknown format code following "\"' 'format du code suivant "\" inconnu'
Vous spécifiez un code de format dans une chaine, qui est illégal.
Voir le chapitre 2F pour une liste des codes de format.
- '/* not properly nested comment structure */'
'/* commentaires mal imbriqués */'
Le nombre de '/*' n'est pas égal au nombre de '*/', ou placé dans un
ordre bizarre.
- 'could not load binary' 'ne peut pas ouvrir la bibliothèque'
le fichier spécifié dans le ligne INCBIN <fichier> ne peut pas être lu.
- '"}" expected' '"}" nécessaire'
Vous avez commencé une expression comme '{<var>', et vous avez oublié
le "}".
- 'immediate value expected' 'valeur immédiate nécessaire'
Certaines constructions demandent une valeur immédiate à la place d'une
expression.
Exemple :
DEF s[x*y]:STRING /* mauvais :uniquement quelque chose comme */
/* s[100]:STRING est bon */
- 'incorrect size of value' 'taille de la valeur incorrecte'
Vous spécifiez une valeur trop grande (ou trop petite).
Exemples:
DEF s[-1]:STRING, t[1000000]:STRING /* doit être entre 0..32000 */
MOVEQ #1000,D2 /* doit être entre -128..127 */
- 'no e code allowed in assembly modus'
'pas de code E accepté en mode assembleur'
Vous voulez compiler en mode assembleur, en écrivant 'OPT ASM', et par
accident, vous avez mis du code E.
- 'illegal/inappropriate type' 'type inapproprié/illégal'
A certain endroit ou un <type> est demandé, vous avez écrit quelque chose
d'inaproprié.
Exemple :
DEF a:PTR TO ARRAY /* aucun type de ce type :) */
[1,2,3]:STRING
- '"]" expected' '"]" nécessaire'
Vous avez commencé avec un "[", mais vous avez oublié de terminer
avec un "]".
- 'statement out of local/global scope'
'déclaration en dehors des déclarations locales ou globales'
Un point de déclaration est la première déclaration PROC. Avant ça,
uniquement des définitions globales (DEF, CONST, MODULE, etc) sont
permises, et aucun code. Dans la secondes parties, seulement les
définitions de code et de fonctions sont légales, aucune définition
globale n'est possible.
- 'could not read module correctly' 'ne peut lire le module correctement'
Une erreur du DOS est survenue en voulant lire un module à partir de la
déclaration MODULE. Causes :
- 'Emodules:' est mal assigné,
- le nom du module est mal écrit, ou n'éxiste pas,
- vous avez écrit MODULE 'bla.m' au lieu de 'bla' (sans suffixe).
- 'workspace full!' 'espace de travial plein !'
N'arrive que rarement. Si cela survient, you devez mettre l'option '-m'
pour forcer manuellement EC à faire une estimation plus large sur la
quantité de mémoire nécessaire.
Essayez de compiler avec -m2, puis -m3 jusqu'à ce que l'erreur
disparaisse. Vous aurez certainement écrit une gigantesque application
avec une grande quantité de données pour arriver à cette erreur.
- 'not enough memory while (re-)allocating'
'pas assez de mémoire pendant la (ré)allocation'
Tout simplement. Solutions possibles :
1. vous avez plusieurs programmes marchant en multitache. Quittez en
quelques uns et reéssayez.
2. il vous reste peux de mémoire, ou votre mémoire est fragmentée.
3. aucun des 2 possibilités. Achetez de la mémoire (Hum..)
- 'incorrect object definition' 'définition d'objet incorrect'
Vous avez écrit quelque chose en OBJECT et ENDOBJECT qui n'est pas
correct. Voir le chapitre 8F pour savoir comment bien écrire.
- 'illegal use of/reference to object'
'mauvaise utilisation ou référence à un objet'
Si vous utilisez une expressions comme ptr.membre, membre doit être un
membre définit dans l'object ptr.
- 'incomplete if-then-else expression' 'expression if-then-else incomplète'
Si vous utilisez IF comme opérateur (chapitre 4E), alors un ELSE doit
être présent : une expression avec un IF à l'interieur a toujours besoin
de retourner une valeur, alors qu'une déclaration avec un IF peut ne rien
faire si aucun ELSE n'est présent.
- 'unknown object identifier' 'identificateur d'objet inconnu'
Vous utilisez un identificateur qui est reconnu par le compilateur comme
une partie d'un objet, et vous avez oublié de le déclarer. Causes :
- nom mal écrit,
- module manquant,
- l'identificateur dans le module est écrit différemment de ce que vous
pensiez (des RKRM par exemple) Vérifiez avec ShowModule.
NOtez que le système d'objet Amiga hérite des identificateurs de
l'assembleur, pas du C. Les idientificateurs obéissent à la syntaxe
du E.
- 'double declaration of object identifier'
'double déclaration d'idenficateur d'objet'
Un identificateur est utiisé dans le définition de 2 objets.
- 'reference(s) out of 32k range: switch to LARGE model'
'référence(s) en dehors des 32ko : utilisez le grand modèle'
Votre programme est plus grand que 32ko. Mettez simplement 'OPT LARGE'
dans votre code source. Voir le chapitre 16B.
- 'reference(s) out of 256 byte range' 'référence(s) en dehors des 256 octets'
Vous avez probablement écrit BRA.S ou Bcc.S sur un trop grand espace.
- 'too sizy expression' 'expression trop grande'
Vous utilisez une liste, qui est peut-être récursivment, qui est trop
grande.
- 'incomplete exception handler definition'
'définition incomplète du gestionnaire d'exception'
Vous avez certainement utilisé EXCEPT sans HANDLE, ou quelque chose dans
ce genre. Voir le chapitre 13 sur les gestions d'exception.
16F. organisation et allocation des tampons (buffer) du compilateur
-------------------------------------------------------------------
Quand vous revevez une erreur 'workspace full' (espace de travail plein)
(peu probable), ou si vous voulez savoir ce qui se passe quand votre
programme est compilé, il est utile de savoir comment EC organise ses
tampons.
Un compilateur, et c'est le cas d'EC, a besoin de tampons (buffers) pour le
suivi de beaucoup de choses, comme les identificateurs, etc., et pour y
mettre le code généré. EC ne sait pas de quelle taille doivent être les
tampons. Pour certains, ceux par exemple pour garder les constantes, il n'y
a pas de problêmes : si le tampons est plein lors de la compilation, EC alloue
un nouveau tampon et continue. D'autres tampons, comme celui réservé au code
généré, doit être continue en mémoire et ne peut être déplacé pendant la
compilation : EC doit donc faire une bonne estimation de la taille des tampons
pour être capable de compiler des sources de toutes tailles.
Pour faire cela, EC calcule la mémoire nécessaire suivant le cas du source
donné. Dans 99% des cas, EC aura alloué suffisament de mémoire pour compiler
quasiment tous les sources. Dans les autres cas, vous aurez un erreur à la
compilation et vous aurez a spécifier plus de mémoire grace à l'option '-m'.
Essayez les différents sources (de taille et de type différent) en les
combinant avec l'option '-b', pour voir comment ca marche en réalité.
16G. un petit historique
------------------------
Le E n'est pas seulement 'un autre langage' : il a été construit avec
attention et pas à pas par l'auteur du compilateur, parce qu'il n'était pas
content des langages éxistants et particiulièrement de l'horrible code généré
et la lenteur des compilateurs écrit pour eux.
L'Amiga E a pour but premier d'être utilisé comme langage par l'auteur pour
programmer ces programmes Amiga, et il y a réussi. Le E a été développé
intensivement sur un an et demi et n'a certainement pas été le premier
compilateur écrit par l'auteur : certain doivent se rappeler le compilateur
DEX.
Celui-ci tait lent et peu puissant et ne peut être que difficilement comparé
à un compilateur comme l'Amiga E, mais certainement a donné à l'auteur une
expérience qui l'a rendu capable de faire ce que l'Amiga E est aujourd'hui.
Les programmeurs en DEX noterons qu'il est très facile de convertir leurs
anciens sources DEX en E, et continuer le développement avec une puissance
multiplié par 10, et une vitesse par 20. Pour la petite histoire : DEX et le
E se sont dépassés, qudn DEX était terminé, le E était à la moitié de v1.6.
Parceque le E était déja plus performant à l'époque, les exemples de
bibliothèques E et les codes on été transférés en DEX à la demande générale.
Ainsi le prédécesseur a hérité des caractéristiques de son succésseur.
L'auteur écrit aussi de nombreux autres compilateurs et d'interpréteurs,
certains d'entre eux n'ont jamais été distribués.
L'Amiga E est un produit qui sera développé en direction du meilleur langage
pour le système de développement Amiga :
- par implementation des parties manquantes dans la définition du langages :
* Orientation Objet
* meilleur concept des flottants
- en faisant des améliorations spécifiques du compilateur
* génération de code 020/030/881 possible
* optimisation du processus de compilation, et donc possibilité de doubler
la vitesse en ligne par minute annoncé dans le fichier 'compilateur.doc'
* permet la compilation de code programmé en modules, et donc développer de
grande application de manière plus modulaire
- par addition d'élément utile à la distribution
* un éditeur intégré ?
* un debuggeur au niveau du source ?
* des outils comme CASE, par exemple
- par correction des bugs (quel bugs?!) 8*-)
F I N ! Ouf ! Bonne programmation... en E, bien sûr !