Caractéristiques
Syntaxe
ooc, tout comme le Python, a été conçu pour être lisible, et pour être facilement écrit par un humain dans un simple éditeur de texte. Les blocs sont délimités par des accolades, comme en C.
Le principe DRY est au cœur de la syntaxe ooc. Par exemple, l'opérateur := permet de déclarer une variable à partir de l'expression à droite, en inférant son type:
| Déclaration de variables en ooc | Déclaration de variables en Java |
|---|
| reponse := 42 chiens := ArrayList new() | int reponse = 42; List chiens = new ArrayList(); |
Types de bases
ooc possède les mêmes types de bases que le langage C, toutefois, tous les types commencent par une majuscule, par convention.
La bibliothèque standard offre également de nombreuses structures de données telles que les tableaux-listes, les listes chaînées, les tables de hachages, les piles, etc. Les tableaux sont inclus dans le langage et, contrairement aux pointeurs C, sont sûrs (on ne peut y accéder en-dehors de leurs bornes).
On peut créer des types en définissant une classe, une interface, ou en faisant une cover d'un type défini en C.
Programmation objet
Héritage simple
Le modèle orienté objet d'ooc supporte l'héritage simple.
Animal: class { faireDuBruit: abstract func } Chien: class extends Animal { faireDuBruit: func { "Wouf!" println() } } Chat: class extends Animal { faireDuBruit: func { "Miaou!" println() } } toucher: func (animal: Animal) { animal faireDuBruit() } toucher(Chien new()) toucher(Chat new())
Interfaces
Les interfaces permettent un semblant d'héritage multiple, à l'instar du Java.
Representable: interface { toString: func -> String } Printable: interface { print: func } Document: class implements Representable, Printable { data: String init: func (=data) {} toString: func -> String { data } print: func { toString() println() } }
Programmation fonctionnelle
ooc supporte les fonctions anonymes et les fermetures. On peut manipuler les fonctions comme n'importe quelle valeur, grâce à une syntaxe claire et lisible :
import structs/[ArrayList, List] filter: func (list: List, f: Func (Int) -> Bool) -> List { copy := ArrayList new() for (x in list) if (f(x)) { copy add(x) } copy } list := [7, 1, -3, 2, -12, 3, 42] as ArrayList list2 := filter(list, |a| a > 0 && a < 20) print(list); print(list2) print: func (list: List) { for (x in list) (x + ", ") print() println() }
Cet exemple utilise également des types génériques, montre une manière simple de créer des tableaux-listes (ArrayList), de parcourir des objets itérables, de retourner implicitement des objets, ainsi que la syntaxe courte pour les fonctions anonymes.
Modularité
Chaque fichier .ooc correspond à un module qui peut être importé depuis n'importe où. Les modules peuvent être organisés en paquetages (les dossiers où ils sont placés). Il est possible d'importer un module dans un espaces de noms donné, par exemple
// on importe le module File dans l'espace de noms IO. import io/File into IO // on peut définir sa propre classe sans se soucier des conflits File: class {} // on utilise la classe importée IO File new("C:\\windows\\iexplore.exe") remove()
Contrairement au C il n'y a pas de fichiers d'en-tête séparés des fichiers sources. import, plutôt que de copier/coller le contenu du fichier, définit une dépendance entre deux modules.
Le compilateur ayant ainsi une vue claire des dépendances entre modules, il supporte la recompilation partielle sans besoin de fichiers de projets du type Makefile.
Les fichiers .use permettent également de spécifier des dépendances sur des bibliothèques externes, des options à passer au compilateur C, des informations de versions, etc. La directive use permet d'en faire usage dans du code ooc. Ainsi, utiliser par exemple la SDL, OpenGL est l'affaire d'une ligne de code source.