1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2004-2008 Jean Privat <jean@pryen.org>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Exemple commenté d'un programme en NIT
18 # Cet exemple est tiré du dernier TP C++ de l'année des IUP1
20 # Attention, NIT est un langage enrichi en sucre (syntaxique) et en déclaration
21 # implicite. Cet exemple en introduit petit à petit. Et chaque utilisation est
22 # abondamment commentée. D'ailleurs, a propos de commentaires, il est peut-être
23 # trop tard pour dire que ceux-ci commencent par un dièse (#)
25 # Les points suivants sont couverts par cet exemple :
26 # - définition des modules
27 # - définition des classes
28 # - définition des propriétés (attributs, procédures, fonctions)
29 # - structures de contrôles (blocs, boucles, conditions)
31 # - accesseurs universels
32 # - constructeurs et constructions d'instances
33 # - chaînes de caractères
35 # Les points suivants sont aperçus :
36 # - classes génériques
37 # - protection (visibilité des propriétés)
39 # - héritage et redéfinition
41 # Les points suivants ne sont pas abordés :
42 # - accesseurs implicites
43 # - (re)définition des opérateurs
44 # - paramètres implicites
45 # - méthodes à arité variable
46 # - méthodes retardées (virtuelles pures)
47 # - constructeurs universels
50 # - covariance des types
51 # - raffinement de classes
53 # Un fichier = un module. Les classes d'un module sont définies dans le fichier.
54 # Au début des fichiers, les modules à importer doivent être déclarés (via le
55 # mot clé "import"). Implicitement, tout module importe le module nommé
56 # "standard" qui définit les classes usuelles.
58 # On va étudier une représentation d'un entrepôt de produits variés.
61 # Dans un bloc "private", les propriétés déclarées sont seulement accessibles au
64 # Les attributs sont déclarés par le mot clé "fun" et commencent par un "@"
65 var nom_
: String is noinit
# Un entrepôt a un nom (de type chaîne).
66 var rayons
: Array[Rayon] is noinit
# Il est composé d'un ensemble de rayon.
67 # "Array" est une classe paramétrée, les crochets en sont la marque.
68 # La classe "Rayon" est définie plus loin
71 # Les propriétés déclarées dans un bloc "public" sont accessibles à tout le
74 # Les méthodes (fonctions et procédures) sont déclarées par le mot clé "fun"
75 # nom est une fonction qui retourne une chaîne
78 # "return" sort de la fonction
80 # En fait, les attributs et les méthodes sont dans deux espaces
82 # Le @ peut se prononcer "at" ce qui rappelle le mot attribut
85 # set_nom est une procédure qui prend une chaîne en paramètre
86 fun set_nom
(n
: String)
88 _nom_
= n
# "=" désigne l'affectation
89 # Les affectations sont des instructions et ne sont pas
93 fun add_rayon
(r
: Rayon)
95 _rayons
.add
(r
) # "add" ajoute un élément
98 # to_s est la méthode implicitement appelée par "print" et "println"
99 # Il s'agit de la représentation "humaine" d'un objet
100 redef fun to_s
: String
102 # Les variables sont déclarées par "var", leur portée va de leur
103 # déclaration jusqu'au "end" correspondant
104 var s
= new FlatBuffer # Là où on calcule le résultat
105 # Les chaînes littérales sont déclarées avec des guillemets
106 s
.append
("*** Entrepôt ") # On initialise "s"
107 # puis on concatène des chaînes à "s"
108 s
.append
(_nom_
) # la méthode "append" concatène
110 # Les conditions sont de la forme "if then else if else end"
111 if _rayons
.is_empty
then
112 s
.append
("L'entrepôt est vide\n")
114 var i
: Iterator[Rayon]
115 # Les itérateurs permettent de traverser les collections
116 i
= _rayons
.iterator
# "iterator" retourne un nouvel itérateur
117 # La forme des boucles est "while do end"
118 while i
.is_ok
do # on pointe un élément valide ?
119 # "item" retourne l'élément pointé par
121 s
.append
(i
.item
.to_s
)
122 i
.next
# passe à l'élément suivant
124 s
.add
('\n') # "add" ajoute un caractère à la fin.
125 # Comme en C, les caractères sont entre simples cotes.
131 # Dans un bloc "constructor", les méthodes déclarées sont celles utilisées pour
132 # créer des instances
134 # init sans paramètre est le constructeur implicite
138 # "new" permet d'instancier une classe.
139 _rayons
= new Array[Rayon]
140 # Un nouveau tableau est implicitement vide
141 # On aurait pu prévoir une capacité en utilisant un autre
142 # constructeur et en écrivant :
143 # _rayons = new Array[Rayon].with_capacity(50)
146 # Plusieurs constructeurs peuvent être définis
147 init with_nom
(n
: String)
150 _rayons
= new Array[Rayon]
153 # Une classe qui ne possède pas de méthode définie dans un bloc "constructor"
154 # est une classe abstraite : elle ne peut être instanciée.
159 var nom_
: String # Désignation du produit
160 var qte_
: Int # Quantité en stock
164 # Comme nous l'avons vu, les accesseurs en lecture sont
165 # généralement homonymes aux attributs (au @ près).
176 # Toutefois, pour les accesseurs en écriture, il est d'usage en NIT
177 # d'utiliser un type de méthode particulier appelé "accesseur universel"
178 # son nom se caractérise par un "=" final
183 # La méthode s'utilise alors de façon élégante :
184 # pour p de type statique Produit, l'expression
186 # correspond à un envoi de message vers la fonction "qte" et retourne
187 # la quantité d'un produit. Tandis que l'instruction
189 # correspond à un envoi de message vers la méthode "qte=" et affecte
190 # la quantité d'un produit.
193 redef fun to_s
: String
195 # On peut aussi utiliser "+" pour concaténer les chaînes
196 return _nom_
+ ":" + _qte_
.to_s
197 # Mais ce n'est pas très efficace car cela crée des objets
202 init(nom
: String, qte
: Int)
211 var stock
: Array[Produit] is noinit
# Des produits en stock
212 var rubrique
: String is noinit
# La catégorie des produits stockés
214 # Cette fonction est utilisé par to_s pour afficher un petit titre
215 fun to_s_head
: String
217 # Les déclarations de type sont optionnelles dans un bloc "var".
218 # Si une expression est passée comme valeur initiale d'une
219 # variable, le type statique de la variable est implicitement
220 # celui de l'expression.
221 var s
= new FlatBuffer
222 s
.append
("* Rayon : ")
223 # Ici, le type statique de s est implicitement String
230 fun cherche_produit
(n
: String): nullable Produit
232 var i
= _stock
.iterator
235 # "=" est l'opérateur égalité de valeur.
241 # "null" correspond à l'objet vide
246 redef fun to_s
: String
248 var s
= new FlatBuffer
250 # Les boucles en NIT sont des structures puissantes, toutefois
251 # la manipulation des itérateurs peut être facilité par la
254 # L'expression de droite doit être sous-type de Collection
255 # (ce qui est le cas pour Array[Produit]).
256 # La variable p est automatiquement déclarée, son type
257 # automatiquement déduit (ici Produit) et la collection est
258 # automatiquement parcourue
259 # En réalité, la structure "for in" n'est que du sucre
260 # syntaxique pour une boucle sur une collection via des
261 # itérateur comme celle écrite dans la fonction
262 # "cherche_produit" précédente
270 # Cette fonction permet de retourner la quantité d'un produit donné
271 # Ce service est très simple à implémenter si on utilise cherche_produit
272 fun quantite
(n
: String): Int
274 var p
= cherche_produit
(n
)
275 # "==" est l'opérateur d'égalité d'identité
283 # Le service dual consiste à définir la quantité d'un produit
284 # En NIT, il est d'usage de définir ce genre de service par un accesseur
286 fun quantite
=(n
: String, i
: Int)
288 var p
= cherche_produit
(n
)
290 # On Construit un nouveau produit que l'on ajoute
291 _stock
.add
(new Produit(n
, i
))
293 # On change la quantité du produit trouvé
297 # Les accesseurs universels s'utilise de façon toujours aussi élégante.
298 # Pour r de type Rayon, on écrira donc :
299 # r.quantite("clous")
300 # pour obtenir la quantité de clous dans le rayon, et :
301 # r.quantite("clous") = 15
302 # pour mettre le nombre de clous à 15
311 _stock
= new Array[Produit]
316 # Les super-classes sont déclarés avec le mot clé "special".
317 # Implicitement, c'est "Object", la classe de tous les objets.
320 # Tant qu'on parle d'implicite, en l'absence de bloc de propriétés,
321 # celles-ci sont déclarées en tant que "public"
324 return _temp_max_
# Attribut défini juste en dessous
327 var temp_max_
: Int # Une température maximale
328 # Les autres attributs sont bien sûr hérités !
330 redef fun to_s_head
: String # On redéfinit cette méthode
332 # nous avons vu le "append" et le "+" sur les chaînes, mais la
333 # méthode la plus simple reste la construction "{}" qui
334 # permet l'inclusion automatique d'expressions. C'est aussi la méthode
336 return "* Rayon Réfrigéré : {_rubrique} - t° max : {_temp_max_}\n"
340 init(r
: String, t
: Int)
344 _stock
= new Array[Produit]
350 # Le main (point d'entrée du programme) peut être déclaré directement à la fin
351 # des définitions de classes
353 # Constructeur implicite ("init")
355 # "println" affiche un truc et ajoute un \n à la fin
356 print
(e
.nom
) # affiche "sans nom"
357 # "print" affiche un truc mais sans le \n à la fin
358 printn
('\n') # va à la de ligne
360 e
.set_nom
("Montpellier")
361 printn
(e
.nom
, "\n\n")
362 # Affiche "Montpellier" et saute une ligne.
363 # Contrairement à "println", "print" peut prendre plusieurs paramètres
365 # Constructeur explicite ("with_nom")
366 e
= new Entrepot.with_nom
("Lunel") # on perd l'ancien entrepôt
368 # affiche "*** Entrepot Lunel *** L'entrepôt est vide"
370 var p
= new Produit("Carotte", 15)
371 print
(p
) # affiche Carotte:15
373 print
(p
) # affiche Carotte:20
376 var r
= new RayonNormal("Légumes")
379 r
.quantite
("Carotte") = 15
380 r
.quantite
("Navet") = 10
381 r
.quantite
("Chou") = 3
383 r
.quantite
("Chou") = 13
384 r
.quantite
("Courge") = 1
389 var r2
= new RayonFroid("Surgelés", -5)
391 r2
.quantite
("Pizza") = 12
392 r2
.quantite
("Poisson pané") = 4