Ash
Un langage de script simple et concis avec support pour l'orienté objet.
Sommaire
- Introduction
- 1. Noyau du langage
- 2. Types de base et leurs opérateurs
- Booléen
- Entier et réel
- Chaîne
- Liste
- Dictionnaire
- 3. Instructions / Contrôle du flux
- Séquence
- Sélection
- Boucle
- 4. Sous-programmes
- Fonctions
- Procédures
- Getters
- 5. Classes et objets
- 6. Gestion des erreurs
- 7. Modules
- 8. Bibliothèque standard
- 9. Implémentations et interfaces
- 10. Liste des éléments du langage
- 11. Eléments de style
Introduction
Ce document décrit le langage de script Ash. Il se veut simple et concis.
Vous trouverez dans ce document une présentation de ses fonctionnalités ainsi que des encarts expliquants les choix de design qui ont été fait. En effet, je pense que pour mieux comprendre un artefact généré par l'homme, qui résulte d'un ensemble de décisions, il vaut mieux savoir les raisons derrière celles-ci. On évoquera ainsi ce qui a été écarté et ce qui est envisagé comme évolution possible.
Ash se veut toutefois un langage assez stable, en réaction à à la complexification et à l'accélération des différents langages de programmation qui se mettent à changer tous les ans, même les plus anciens, en ajoutant à chaque fois plus de fonctionnalités, de syntaxes, de façons différentes de faire la même chose. Toutefois, étant l'œuvre d'une seule personne, il se peut que je n'implémente pas tout ce que je voulais, d'où les évolutions possibles mentionnées.
1. Noyau du langage
1.1 Installation
Pour lancer coder avec Ash, il existe trois façons :
- Via l'interpréteur interactif en lançant ash.exe sans argument. La forme générale est ash.exe [arguments] [script] [arguments du script]. L'invite de commande >>> apparaît et on peut directement écrire à la suite des instructions et des expressions qui seront immédiatement évaluées. Il faut taper exit pour sortir de l'interpréteur. Si voulez exécuter un script depuis l'interpréteur, il faut employer la fonction exec('chemin du script') qui exécutera le script dans l'espace de noms actuels. Pour exécuter directement une ligne utiliser l'option -e puis entre guillemets le code.
ash.exe
- Via l'exécution d'un script, stocké dans un fichier .ash (ex : pipo.ash). On lance ash.exe en passant le chemin vers le ficher en paramètre. Si vous voulez utilise l'interpréteur à la suite de cette exécution, il faut utiliser l'option i. On peut passer des arguments au script après celui-ci.
ash.exe pipo.ash
ash.ex -i pipo.ash
- Via l'exécution d'un script, stocké dans un fichier .ash, qu'on transpile en Python à l'aide de ash.exe. On exécute ensuite le script Python produit à l'aide de l'interpréteur Python.
ash.exe -transpy pipo.ash python pipo.py
Les arguments sont stockés dans une liste. 0 est le nom du script. En nég, les options préalables. En pos, les options du script.
Note de design
Ces encadrés exposent des décisions de design.
La syntaxe de Ash s'inspire des langages Lua, Ruby et Pascal.
Les langages JavaScript et Python sont également des inspirations pour leur fonctionnement et leur bibliothèque de base.
1.2 Identifiants, valeurs et littéraux
Comme premier programme lorsque l'on apprend un langage de programmation, la tradition veut qu'on affiche "Bonjour le monde !" sur la sortie standard. Pour cela, il faut lancer l'interpréteur interactif ash.exe puis taper :
writeln("Hello World!")
Vous venez d'appeler une fonction, nommée writeln, avec un paramètre, la chaîne de caractère "Hello World!". En exécutant ce code, la chaîne sera affichée sur la sortie standard.
Ash est un langage de script qui fonctionne avec des identifiants et des valeurs. Un identifiant référence une valeur. Une valeur est exprimée directement dans un code source par un littéral. Pour déclarer un nouvel identifiant et la valeur qu'il référence, on utilise l'opérateur =
en mettant à gauche l'identifiant et à droite la valeur :
a = 5
Ici a
est l'identifiant et 5
le littéral qui désigne l'entier 5. De même, la chaîne "Hello World!" est un littéral de type chaîne de caractères. Un littéral est une constante, il n'est pas modifiable, qui possède un type. Un littéral ne peut jamais apparaître à gauche de l'opérateur =
.
Plusieurs identifiants peuvent référencer la même valeur, dans le code ci-dessous les identifiants a
, b
et c
référencent la même valeur :
a = 5 b = 5 c = b -- équivalent à c = a
Un identifiant peut, au cours d'un programme, référencer différentes valeurs mais à un instant donné il n'en référencera toujours qu'une :
a = 5 a = "Hello World"
Ash fait la différence entre les minuscules et les majuscules, il est sensible à la casse, ainsi a
et A
ne sont pas les mêmes identifiants.
Un identifiant peut être associé à un type lors de sa déclaration (= la première qu'on l'utilise). Dans ce cas, il ne pourra référencer que des valeurs de ce type. Pour cela on utilise l'opérateur :
suivi du type :
a : int = 5 a = 8 -- ok 8 est de type int a = "Hello World" -- déclenchera une erreur
On peut obliger un identfiant à ne référencer qu'une seule valeur et ne pas pouvoir en changer lors de sa déclaration. Pour cela, on utilise le mot-clé const
.
const a : int = 5 -- on peut mettre le type const b = 6 -- mais sinon il sera déduit du type de l'expression à droite du = a = 22 -- erreur b = "abc" -- erreur
Enfin, on peut déclarer un identifiant qui ne référence aucune valeur avec le mot-clé nil
:
i = nil
On ne peut jamais utiliser un identifiant avant qu'il ne soit défini.
1.3 Instructions et expressions
Un programme Ash est composé d'instructions et d'expressions.
Seules les expressions peuvent être évalués : 2 + 3 est une expression qui peut être évaluée à l'entier 5.
Un bloc est un ensemble composée d'instructions et/ou d'expressions. Un bloc n'est pas évaluable.
Exemple de l'instruction while
:
while expression do bloc loop
Cette instruction exécutera le bloc tant que l'expression entre le while
et le do
sera vraie.
Une instruction utilise des mots-clés : ici, while
, do
et loop
.
1.4 Mots-clés
Ash utilise 27 mots-clés.
var | const | |||
if | then | elsif | else | end |
while | do | loop | for | in |
break | next | |||
fun | pro | return | ||
and | or | not | ||
class | static | is | ||
import | ||||
true | false | nil |
1.5 Constantes et variables
Pour déclarer une variable, il faut utiliser cette syntaxe :
[var] ID [: TYPE] = EXPR
Il est recommandé de commencer une variable par une minuscule et d'utiliser un "_" pour séparer les mots.
var age_personne : nat = 25
Pour déclarer une constante de référence, il faut utiliser cette syntaxe :
const ID [: TYPE] = EXPR
Cela indique que l'identifiant ID
référencera toujours la valeur donnée par EXPR
. Toutefois, cette valeur peut-être elle-même constante (comme le littéral 5
) ou modifiable.
Il est recommandé d'écrire les constantes en majuscule.
const PI : num = 3.14
nom de variable : (_A-Za-z)(0-9A-Za-z)
_ is for dummy variable or the last expression. utf-8 variable are supported.
following word are reserved because they are keyword.
defined?(tst) --> nil tst = nil --> variable defined with value if tst is nil then writeln('tst is nil') end tst = 22 writeln(tst) --> 2
A variable is automatically cleaned after its containing scope is destroy (function or prog body).
1.6 Types et opérateurs
Une expression est forcément évaluable à une valeur avec un type :
5 + 2
Cette expression donne 7, de type nat pour les entiers naturels.
Une expression est composée d'opérateur et d'opérande.
Certains opérateurs sont binaires (= 2 opérandes) ou unaire (= 1 opérante).
Certains opérateurs s'appliquent à certains types de valeurs.
Il n'y a pas d'opérateur postfixe en Ash.
Une expression peut être composée de sous-expression :
exp op exp op exp
Séparateurs d'expression : \n, ;
Liste des opérateurs :
Priorité | Opérateurs | Description |
---|---|---|
1 | > | extraction d'import, type de retour des fonctions |
1 | < | héritage |
1 | = | affectation |
7 | +=, -=, *=, **=, /=, //=, %= | affectation combinée |
7 | +, -, *, **, /, //, % | mathématiques |
1 | . | call |
6 | ⩵, ≠, >, ⩾, ⩽, < | comparaison |
3 | and or not | booléen |
2 | << >> | binaire |
# | longueur d'une séquence | |
$ | conversion en chaînes de caractères | |
${x} | interpolation dans les chaînes de caractères |
1.7 Un programme plus complexe
Une autre tradition est le calcul d'une factorielle. Ouvrez un fichier texte, enregistrez-le sous le nom de fact.ash puis tapez dedans :
-- fonction factorielle récursive fun fact(nb : int) if nb == 0 then return 1 elsif nb > 0 then return nb * fact(nb - 1) else return Exception("Invalid number") end end nb = input('enter a number:') writeln(fact(nb))
1.8 Commentaires
Les commentaires monolignes commençent par --
(comme Lua et SQL) et s'étendent jusqu'à la fin de la ligne.
Les commentaires multilignes commençent par --[[
et finissent par --]]
(comme Lua)
Note de design
Cela permet de conserver //
(C, JavaScript) et #
(Ruby, Python) comme opérateurs de division entière et longueur respectivement.
-- comment --[[ start of multi line ]] end of multi line
2. Types de base et leurs opérateurs
Ash utilise 9 types de données de base :
Nom | Type | Littéral |
---|---|---|
Booléen | Boolean, boolean, bool | true false |
Entier | Integer, integer, int | 25 -25 0b010 0xAB |
Naturel | Natural, natural, nat | 25 |
Nombre | Number, number, num | 2.5 -2.5 |
Chaîne | String, string, str | "bonjour" |
Tableau | Array, array | {val1, val2, val3} |
Liste | List, list | [val1, val2, val3] |
Dictionnaire | Dict, dict | {key: value} |
Valeur nulle | Nil, nil | nil |
2.1 Booléen
2.2 Entier et réel
2.3 Chaîne
2.4 Liste
Signature |
---|
pro flatten() |
pro pop, shift() : any |
pro insert(index : int, value : any) |
pro add, append, push(value : any) |
get first : any |
get last : any |
fun find, index(value : any) : nat |
fun find_all(value : any) : [int] |
fun include?, has? contains?(value : any) : bool |
get min : any |
get max : any |
get length, count, size : nat |
pro sort() |
pro reverse() |
pro uniq() |
2.5 Dictionnaire
3. Instructions / Contrôle du flux
3.1 Séquence
Un bloc est un ensemble d'instructions ou d'expressions. Les instructions et expressions sont séparées par des nouvelles lignes ou des points virgules.
a = 1 + 4 2 -- valide a = 1 + 4 ; 2 -- valide (équivalent aux 2 lignes du dessus) a = 1 + 4 2 -- ceci n'est pas valide en ash
3.2 Sélection
if CONDITION then ACTION [elsif CONDITION then ACTION]* [else ACTION] end
3.3 Boucle
while CONDITION do ACTION loop for ID1[, ID2] in ITERABLE [if CONDITION] do ACTION loop break next
4. Sous-programmes
4.1 Fonctions
fun[ction] ID (P1 [: T1], P2 [: T2]) [: TYPE] ACTION return EXPR end
4.2 Procédures
pro[cedure] ID (P1 [: T1], P2 [: T2]) ACTION return end
4.3 Getters
Un getter est une fonction qui ne prend pas de paramètres. Elle sert à retourner des valeurs qui sont calculées. On évitera de mettre des calculs trop importants dans un getter.
get ID : TYPE ACTION return EXPR end
5. Classes et objets
class ID [< SUPER] [static const, static var, [var]] ID = EXPR [static] fun or pro pro init(@a int, b int) @b = b end end
Un attribut est une variable attachée à une instance particulière, on le préfixe par @ à l'intérieur des méthodes d'une classe. Lors de sa déclaration en dehors d'une fonction, on ne le préfixe pas :
class personne age : int name : string fun hello() : void writeln("Bonjour, je suis ${@name} et j'ai ${@age} ans") end end
self super a = ID.new(param) class Point object (fixed fields)/obj x int y int end @attr -- instance attribute $attr -- class attribute (en Ruby pour global et @@ pour class) if o is Class c then -- vous pouvez utiliser c dans ce bloc end static class Order < String -- no instance <=> enum static Move = "Move" static Wait = "Wait" static Attack = "Attack" static Follow = "Follow" end
6. Gestion des erreurs
return EXCEPTION fun div(a : num, b : num) : num if b == 0 then return DivByZeroException else return a / b end end div(5, 0) if DivByZeroException then writeln("An exception has been catched") end
7. Modules
import SYMBOL[.SYMBOL] [include ELEM1[, ELEMi]] import FILEPATH as SYMBOL [include ELEM1[, ELEMi]]
Accès via symbol.membre
Un fichier = un module
Pour l'importer : require str ex :
- require "flat.ash"
- require "../flat.ash"
module ID < SUPER -- pas d'instanciation ! -- permet de faire : -- des classes statiques -- des interfaces end
8. Bibliothèque standard
ID [module] = import (FILEPATH | ID) [ > ID1, ID2 ] module ID -- (en tête de fichier, pas de fin) (plutôt que package ou namespace)
8.1 Noyau
Object
o.clone
o.methods
o.respond_to?
o.dump
o.load
Hash
d.delete
d.find
d.keys
d.values
8.2 Console
writeln
write
8.3 Fichiers
8.4 Système d'exploitation
9. Implémentations et interfaces
9.1 Ash/Python
9.2 Ash/JavaScript
9.3 Ash/C
9.4 Ash/PHP
9.5 Ash/Lua
10. Liste des éléments du langage
Type | Eléments |
---|---|
Séparateurs | ( ) [ ] { } , ; |
11. Eléments de style
- snake_case pour les méthodes, les attributs et les variables,
- PascalCase pour les classes,
- MAJUSCULES pour les constantes.