La Syntaxe
Pour Haxe toutes les expressions ont le même poids. Cela veut dire qu'il est possible de les emboiter sans problème. L'expression,"foo(if (x==3) 5 else 8)", est donc parfaitement valide. Cette exemple montre aussi que toutes les expressions retournent une valeur d'un type donné.
Constantes
Les constantes suivantes peuvent être directement utilisées dans Haxe:
0; // Int -134; // Int 0xFF00; // Int 123.0; // Float .14179; // Float 13e50; // Float -1e-99; // Float "hello"; // String "hello \"world\" !"; // String 'hello "world" !'; // String true; // Bool false; // Bool null; // Unknown<0> ~/[a-z]+/i; // EReg : Expression Régulière
Vous noterez que null
est spécial et peut être utilisé pour n'importe quel type. Il a un comportement différent de celui du type Dynamic
. Ce point sera développé plus en détails dans le chapitre introductif sur l'inférence de type.
Operations
Les opérations suivantes peuvent être utilisées dans Haxe (elles sont présentées par ordre de priorité) :
v = e
: assigne une valeur à une expression, retourne icie
+= -= *= /= %= &= |= ^= <<= >>= >>>=
: assigne après avoir effectué l'opération correspondantee1 || e2
: Sie1
esttrue
alorstrue
sinon évaluee2
.e1
ete2
doivent tous deux être du typeBool
.e1 && e2
: Sie1
estfalse
alorsfalse
sinon evaluee2
.e1
ete2
doivent tous deux être du typeBool
.e1...e2
: Construit un itérateur d'entiers (voir plus loin le chapitre sur les itérateurs).== != > < >= <=
: effectue une comparaison classique ou physique entre deux expressions du même type, retourne une valeur du typeBool
.| & ^
: Effectue les opérations correspondantes au niveau du bit entre deuxInt', retourne un
Int''.<< >> >>>
: Effectue les glissements de bits correspondants entre deuxInt
, retourne unInt
.e1 + e2
: Additionne les deux valeurs retournées par les expressionse1
ete2
. Si les deux valeurs sont desInt
, retourne unInt
. Si une valeur est du typeInt
et l'autre du typeFloat
, retourne unFloat
. Dans les autres cas, retourne unString
.e1 - e2
: Soustrait les deux valeurs retournées par les expressionse1
ete2
. Si les deux valeurs sont desInt
, retourne unInt
. Si une valeur est du typeInt
et l'autre du typeFloat
, retourne unFloat
.e1 * e2
: Multiplie les deux valeurs numériques retournées par les expressionse1
ete2
. Les règles de retour sont celles de la soustraction.e1 / e2
: Divise deux nombre, returne unFloat
.e1 % e2
: Retourne le modulo de deux nombres en respectant les règles de la soustraction.
Operations Unaires
Les opérations unaires sont possibles dans Haxe :
!
: Opération binaire not. Inverse la valeur booléenne de l'expression.-
: Nombre négatif, change le signe d'unFloat
ou d'unInt
.++
et--
peuvent être positionnés avant ou après une expression. En pré-position, retourne la valeur incrémentée de l'expression. En post-position, retourne la valeur de l'expression et incrémente seulement après. Ces opérations ne peuvent être utilisées que sur lesInt
et lesFloat
.~
: complémentaire binaire d'unInt
.
Note: ~
est habituellement utilisé pour les entiers sur 32 bits. Il ne produira donc pas les resultats attendus avec Neko 31-bits, c'est pour cela qu'il ne fonctionne pas avec Neko.
Parenthèses
Les expressions peuvent être entourées de parenthèses afin de lui donner une priorité spéciale lors de l'évaluation. Le type de (e)
est le même que celui de e
et retournent la même valeur.
Blocs
Les blocs exécutent plusieurs expressions. La syntaxe d'un bloc est la suivante :
{ e1; e2; // ... eX; }
Un bloc retourne la valeur, et est du type, de la dernière expression qu'elle contient. Par exemple :
{ f(); x = 124; true; }
Ce bloc est du type Bool
et est évalué à true
.
Le bloc vide {}
est une exception. Il est évalué en tant que Void
.
Variables locales
Les variables locales peuvent être déclarées dans un bloc en utilisant l'instruction var
, comme dans l'exemple suivant :
{ var x; var y = 3; var z : String; var w : String = ""; var a, b : Bool, c : Int = 0; }
Une variable peut être déclarée avec un type et une valeur optionels. Sans valeur, la variable est initialisée comme null
. Sans type, la variable est initialisée comme du type Unknown
mais est strictement typée. Plus de détails sont donnés dans l'introduction à l'inférence de type.
Plusieurs variables peuvent être déclarées en même temps dans la même expression.
Les variables locales ne sont accessibles qu'à l'intérieur du bloc dans lequel elles ont été déclarées.
Identificateurs
Quand un identificateur est rencontré, il est résolu avec les règles suivantes, dans l'ordre :
- L'identificateur est la dernière variable locale ayant la priorité
- L'identificateur est le membre d'une classe (celui de la classe courante ou appartenant à une classe héritée)
- L'identificateur est un champ static de la classe courante
- L'identificateur est un constructeur d'une énumération qui a été déclarée dans le même fichier ou qui a été importée.
enum Axis { x; y; z; } class C { static var x : Int; var x : Int; function new() { { // x est ici le membre de la classe : this.x var x : String; // maintenant x est la variable locale déclarée dans le bloc } } function f(x : String) { // ici x est le paramètre de la fonction } static function f() { // x est ici la variable statique } } class D { function new() { // x fait référence eu constructeur de l'énumération: l'axe x } }
Le type des identificateurs est résolu en fonction des paquets importés (voir un peu plus loin).
Acces aux champs
Les objets sont accessibles avec la notation classique par point :
o.field
Appels
Les fonctions sont appellées en utilisant des parenthèses et en séparant les arguments par des virgules. Les méthodes sont accessibles en utilisation un point :
f(1,2,3); objet.methode(1,2,3);
New
L'instruction new
est utilisée dans une expression pour créer une instance d'une classe. Elle requiert un identificateur de classe et accepte des paramètres.
a = new Array(); s = new String("hello");
Tableaux
Il est possible de créer un tableau à partir d'une liste de valeurs en utilisant la syntaxe suivante :
var a : Array<Int> = [1,2,3,4];
Il faut noter que le type Array
requiert un paramètre de type qui détermine le type des éléments du tableau. De cette manière, toutes les opérations sur les tableaux sont sécurisées. Par conséquent, tous les éléments d'un tableau doivent avoir le même type :
L'accès aux éléments du tableau se fait par l'intermédiaire de la notation classique avec des crochets :
premier = a[0]; a[1] = valeurDuSecond;
L'indexation se fait par un entier du type Int
.
If
Voici quelques exemples de conditions avec if :
if (vies == 0) poubelle(); if (drapeau) 1 else 2;
Voici la syntaxe générique pour if :
if( expr-cond ) expr-1 [else expr-2]
L'expression expr-cond
est évaluée en premier. Elle doit être du type Bool
. Si elle est true
alors expr-1
est évaluée, sinon if expr-2
est évaluée dans le cas où elle est présente.
S'il n'existe pas de else
et que l'expression conditionnelle est false
alors l'expresson if
est du type Void
. Si else
est renseignée, alors les expressions expr-1
et expr-2
doivent être du même type et sera le type de l'expression if
:
var x : Void = if( drapeau ) poubelle(); var y : Int = if( drapeau ) 1 else 2;
Le if
de Haxe est similaire à l'expression a?b:c
en C.
Il existe cependant une souplesse. Si l'expression if
n'est pas supposée retourner de valeur (comme au milieu d'un bloc), les deux expressions expr-1
et expr-2
peuvent avoir des types différents. L'expression, dans ce cas, if
sera du type Void
.
While
Les boucles while
sont standard et requièrent soit une pré-condition, soit une post-condition :
while( expr-cond ) expr-loop; do expr-loop while( expr-cond );
Un exemple de pré-condition :
var i = 0; while( i < 10 ) { // ... i++; }
Un exemple de post-condition :
var i = 0; do { // ... i++; } while( i < 10 );
Comme dans l'expression if
, l'expression expr-cond
doit être du type Bool
.
Voici un autre exemple permettant de décrémenter de 10 à 1 :
var i = 10; while( i > 0 ) { ....... i--; }
For
Les boucles for
sont un peu différentes de leur homologues en C. Elles sont utilisées actuellement pour les itérations introduites plus loin dans un chapitre dédié. Voici un exemple d'itération avec la boucle for
:
for( i in 0...a.length ) { foo(a[i]); }
Return
L'instruction return
est utilisée pour sortir d'une fonction avant la fin du bloc ou pour que la fonction retourne une valeur :
function paire( x : Int ) : Bool { if( x % 2 != 0 ) return true; return false; }
return
peut être utilisée sans argument si la fonction n'a pas besoin de retourner une valeur :
function foo() : Void { // ... if( abandon ) return; // .... }
Break et Continue
Les deux instructions break
et continue
sont utilisées pour sortir et sauter, à l'itération suivante, prématurément dans une boucle :
var i = 0; while( i < 10 ) { if( i == 7 ) continue; // saute l'itération en cours sans évaluer // les expressions suivantes mais continue // l'évaluation de la boucle while if( drapeau ) break; // sort de la boucle definitivement en ignorant // les éventuelles expressions suivantes }
Exceptions
Les exceptions sont un moyen de faire des sauts non locaux. Il est possible de surveiller une exception et de la traiter depuis n'importe quel appel de fonction du bloc mémoire :
function foo() { // ... throw new Error("invalide fonction foo"); } // ... try { foo(); } catch( e : Error ) { // handle exception }
Il peut y avoir plusieurs catch
après un try
pour traiter différents types d'exceptions. Les exceptions sont testées dans leur ordre de déclaration. Traiter Dynamic
permet d'agir sur toutes les exceptions qui n'ont pas déjà été déclarées :
try { foo(); } catch( e : String ) { // Traite ce type d'erreur } catch( e : Error ) { // Traite un autre type d'erreur } catch( e : Dynamic ) { // Traite toutes les autres exeptions }
Toutes les expressions try
et catch
doivent retourner le même type sauf si aucune valeur de retour n'est requise (comme pour if
).
Switch
Les commutations sont un moyen de simplifier les expressions if...else if...else if ...
successives testant la même variable :
if( v == 0 ) e1 else if( v == foo(1) ) e2 else if( v == 65 ) e3 else e4;
Sera traduit par :
switch( v ) { case 0: e1; case foo(1): e2; case 65: e3; default: e4; }
Les commutations dans Haxe sont différentes de leur équivalents dans d'autres langages par le fait que toutes les expressions case
sont cloisonnées - lorsqu'une expression case
est évaluée, la commutation entière est terminée. L'instruction break
est par conséquent inutile et la position de default
n'a pas importance.
Pour certaines plateformes, l'expression switch
a été optimisées pour des tests sur des valeurs constantes (particulièrement les valeurs constantes d'entiers) et sera donc plus rapide à l'exécution.
Les commutations peuvent être utilisées sur les constructions d'une énumération avec une sémantique différente (voir le chapitre dédié).
Fonctions locales
Les fonctions locales (lambda pour ceux qui en ont l'habitude) sont déclarées en utilisant l'instruction function
. Mais elle ne peuvent être nommées. Ce sont des valeurs tout comme le sont les entiers ou les chaines de caractères :
var f = function() { /* ... */ }; f(); // appel de la fonction
Les fonction locales ont accès à leur paramètres, aux champs statiques de la classe courante et aux variables locales déclarées avant elles :
var x = 10; var additionnerAX = function(n) { x += n; }; additionnerAX(2); additionnerAX(3); // x vaut maintenant 15
Cependant, les fonction locales déclarée dans des méthodes ne peuvent avoir accès à la valeur de this
. Pour y parvenir, il faut déclarer une variable locale tampon :
class C { var x : Int; function f() { // provoquera une erreur de compilation var additionnerAX = function(n) { this.x += n }; } function f2() { // Pas de problème de compilation ici var me = this; var add = function(n) { me.x += n }; } }
Objets anonymes
Les objets anonymes peuvent être déclarés comme suit :
var o = { age : 26, nom : "Tom" };
Note : Grâce à l'inférence de type, les objets anonymes sont strictement typés.
«« Types Basiques - L'Inférence de type »»