- 1. Principes généraux du langage talenha
- 1.1. Appel d'une fonction
- 1.2. Les instructions ou commandes
- 1.2.1. if
- 1.2.2. while
- 1.2.3. foreach
- 1.2.4. sandbox
- 1.2.5. select
- 1.2.6. reverse
- 1.2.7. request
- 1.2.8. configure
- 1.2.9. write
- 1.2.10. aspect
- 1.2.11. store et play
- 1.2.12. dispatch
- 1.2.13. compile
- 1.2.14. output
- 1.2.15. dump
- 1.2.16. doc
- 1.2.17. becomes
- 1.2.18. print
- 1.2.19. Affectation
- 1.2.20. Le trapcode
- 1.3. Les expressions
- 1.4. Expression
- 1.4.1. Notation des variables
- 1.4.1.1. Listes
- 1.4.1.2. Champs
- 1.4.1.3. Construire dynamiquement une liste
- 1.4.1.4. Type de données
- 2. Création d'un langage
Principes généraux du langage talenha
Il y a trois syntaxes générales dépendantes du contexte, c'est-à-dire la structure.- Appel à une fonction
- Les instructions ou commandes
- Une expression
Appel d'une fonction
On fait appel à une fonction par son nomnomFonction(paramètre 1, paramètre 2, ...).
Elle est définie dans tous les fichiers dès lors qu'elle est créée par l'instruction suivante :
fun local|global nomFonction[globales](paramètres) { ...corps de la fonction }
Une fonction peut retourner une ou plusieurs valeurs par la commande
output(e, ...)où e est une expression. Lorsque l'on retourne plusieurs valeurs, le résultat de la fonction est une liste. La fonction output ne retourne pas à l'appelant. On peut faire appel à une fonction dans une expression. Le résultat de la fonction est alors partie prenante de l'expression.Dans le corps de la fonction, les variables sont locales à la fonction. Les variables existantes hors de la fonction peuvent lui être transmises en indiquant leurs noms entre crochets dans la définition de la fonction. La fonction est alors transparente pour ces variables. Si la commande comporte le terme 'local' alors toutes les variables sont transmises à la fonction sauf celles entre crochets.
a = 1, print("valeur de a = " a), // a affiche 1 fun f[a]() { a = 2 }, f(), print("valeur de a = " a), // a affiche 2 fun g(a) { a = 3 }, g(a), print("valeur de a = " a) // a affiche 2Un paramètre peut comporter l'option out pour indiquer qu'il s'agit d'un paramètre de sortie. La variable passée en paramètre reçoit alors le résultat calculé par la fonction. Si le paramètre donné n'est pas une variable, le résultat calculé par la fonction est sans effet.
fun f(out a) { a = 1 }, f(a), print("valeur de a = " a), // a affiche 1 fun g(out b) { b = b + 1 }, g(a), print("valeur de a = " a) // a affiche 2
Chaque instruction doit être séparée par une virgule.
L'instructionprint(s, ...)affiche le résultat sur la console de l'application.
La concaténation de textes s'écrit sans opérateur"a" "b" "c"
Les instructions ou commandes
Les instructions sont séparées par une virgule. Si une virgule manque alors la colorisation syntaxique n'est plus effectuée, ce qui permet de détecter cette erreur.L'ensemble des instructions est:
if (expression) { ... code ... } else { ... code ... }
while(expression) { ... code ... }
foreach(e from liste) { ... code ... }
fun local|global nomFonction[global1,global2,...](paramètres) { ... code ... }
sandbox{closure}[globals](paramètres) { ... code ... }
select name { name { ... code ...}, ... }
jump expression
reverse { ... code ... }
request(ihm, paramètre 1, ...)
configure(option=valeur, ...)
write(fichier, liste)
aspect(fichier, expression, after|before|replace, liste)
store(variable, valeur)
play(variable, valeur)
dispatch(fonction, paramètres)
compile(expression)
compile f1 f2 ...
output(e, ...)
dump(e, ...)
doc { description "description" param name "description" global name "description" returns "valeur de retour" }
{ ... code ... } becomes nomFunction{closure}[globals](params)
print(e, ...)
a = expression
nomFonction(expression 1, expression 2, ...)
\objet
if
C'est l'instruction conditionnelle classique. Selon une expression conditionnelle et, lorsqu'elle est évaluée à vrai alors le bloc de code suivant est effectué. Si l'instruction comporte un else et si l'expression conditionnelle est évaluée à faux alors c'est uniquement le bloc de code après le else qui est effectué. Dans les deux cas, les instructions suivantes sont traitées.a = -3, if (a > 0) print("a > 0"), print("valeur de a = " a)
while
C'est la seule instruction du langage permettant d'effectuer une boucle (avec le jump). Tant que l'expression conditionnelle est vrai alors le bloc de code suivant est effectué; à la fin du bloc, l'expression conditionnelle est à nouveau évaluée et si elle est encore vrai alors le bloc de code suivant est à nouveau effectué avec les nouvelles valeurs des variables. Si l'expression conditionnelle devient fausse une seule fois alors, les instructions suivantes sont traitées.counter = 1, while(counter < 10) { print("counter = " counter), counter = counter + 1 }, print("suite...")
foreach
Cette instruction permet d'itérer les éléments d'une liste d'éléments numérotés ou une liste d'éléments nommés.list = [ "a", "b", "c" ], foreach(e from list) { print(e list(e)) } // affiche 0a 1b 2c
hashTable = { a : 1, b : 2, c : 3 }, foreach(e from hashTable) { print(e hashTable(e)) } // affiche a1 b2 c3La variable définie dans le foreach peut être de n'importe quelle forme. Par exemple:
foreach(vars.currentCounter from list) { ... code ... }
sandbox
Il s'agit de la déclaration d'une fonction anonyme qui peut être stockée dans une variable ou bien être appelée pour éliminer des effets de bords et l'utilisation de variables existantes. On peut faire appel à une instruction sandbox avec une fermeture, c'est-à-dire des variables dont la valeur est considérée comme une constante dans la fonction.fun f() { a = 1, output(sandbox{a}[b]() { b = b + a, print("valeur de a dans la fonction = " a) }) }, b = 0, u = f(), \u(), print("valeur de b = " b), print("valeur de a = " a)
Nous verrons plus loin l'explication du backslash (\) devant u().
select
Il permet d'effectuer des goto, des exceptions ou des choix conditionnels (switch).x = 0, select loop { loop { print(x), x = x + 1, jump "test" }, test if (x < 10) jump "loop" }
fun f(x) { if (x >= 10) jump "exception" else { print(x), f(x+1) } } select { default f(0), exception print("end") }
a = "pomme" select { pomme print("pomme"), fraise print("fraise"), framboise print("framboise"), default jump a }
Le paramètre dans l'instruction jump est une expression.
reverse
Elle renverse l'ordre d'interprétation des instructions. Au lieu de lire de haut en bas, les instructions sont lues de bas en haut.reverse { print(a + b), b = a + 1, a = 1 }Cela permet de commencer par le résultat et de terminer par les éléments qui lui sont subordonnés. En terme de lecture, il est plus clair de voir l'ensemble final présenté à la première ligne qu'à la dernière.
request
Définit un contrôle d'interface graphique tel qu'un bouton ou une zone de saisie. La fonction display affiche ces éléments à l'écran.configure
C'est une commande qui active ou désactive des fonctionnalités configurables. Actuellement, seule la fonctionnalité debug est possible.configure(debug=true), configure(debug=false)
write
Ecrit dans un fichier sur le serveur de base de données dans la rubrique "Sorties". Le second paramètre doit être une liste.content = "...", write("result.txt", [ content ])
aspect
Insère avant, après ou remplace la donnée de substitution $name ou #name du fichier la liste indiquée en paramètre.content = "...", aspect("result.txt", #insertion, after, [ content ])
store et play
Stocke dans une variable l'expression indiquée en paramètre. L'instruction play lit le contenu de la variable.store("varName", 1) play("varName", paramètres)
dispatch
Fait appel à une fonction via son nom ou bien anonyme sandbox en indiquant les paramètres dans une liste.dispatch("nomFonction", [ paramètre 1, paramètre 2, ... ]), s = sandbox() { ... code ... }, dispatch(s, [])
compile
Compile un texte ou un fichier et retourne le résultat.compile(unescape("[<<[ print("ok") ]>>]")), result = compile(unescape("[<<[ output("ok") ]>>]")), compile "chemin/fichier"
output
Retourne à la sortie les paramètres indiqués. Dans une fonction, la commande output ajoute une valeur de retour dans la liste de retour. Dans un fichier, la valeur de retour est celle tranmise au résultat de la commande compile.output("valeur 1", "valeur 2", ...)
dump
Cette instruction affiche dans la console de l'application, le contenu interne d'une variable ou d'une expression. Le contenu interne est une structure JSON.Elle permet de savoir ce qu'il y a dans la variable pour pouvoir y lire ses éléments. L'instruction foreach permet aussi de lire tous les éléments de premier niveau. L'instruction dump affiche tous les éléments de tous les niveaux.
a = 1, dump(a)
doc
Documentation qu'il faut placer avant la déclaration d'une fonction pour que cette fonction soit automatiquement ajoutée dans la documentation. Cette documentation est réalisée au moment de l'analyse et de la coloration syntaxique.Les éléments dans l'instruction doc sont :- description suivi d'un texte entre guillemets
- param suivi du nom du paramètre et d'un texte de description entre guillemets
- returns suivi du descriptif de la valeur de retour
- global suivi du nom de la variable globale et d'un texte de description.
becomes
C'est une instruction qui crée une fonction à partir d'une partie d'un code évitant de répéter plusieurs fois le même code à un autre endroit. L'utilisation d'une fermeture permet de traiter les cas où la valeur est connue à l'avance.Le code dans l'instruction becomes est interprété de façon transparente.
La fonction est créée par l'interpréteur de sorte qu'il faut qu'elle aie été définie avant son utilisation ailleurs. Une fois la fonction définie ou redéfinie, le programme connait la fonction.
Elle peut être utile lors du développement du programme.
print(1, 2, 3, 1+2+3)
Affectation
Affecte une expression à une variable. L'expression est évaluée, calculée et le résultat de cette expression est enregistré dans la variable. Reportez-vous au format des variables pour de plus amples informations.Le trapcode
Il s'agit d'une interprétation secondaire utilisée pour le transpilateur. Elle a aussi pour raison d'ajouter la sortie d'une fonction dans la liste de retour.fun f(x) { output(x) }, f(1), // ne retourne rien \f(1) // retourne 1
\f(1)est un raccourci de
output(f(1))
Les expressions
Les opérateurs arithmétiques usuels sont : +, -, *, /, %. La division est toujours décimale. Pour une division entière, il suffit de retirer au dividende le reste de la division avec le diviseur.Les opérateurs de comparaison sont : >, <, >=, <=, ==. Notez que l'opérateur != n'existe pas car l'instruction if suffit.Les opérateurs booléens sont : &&, ||. L'opérateur de négation n'existe pas.L'opérateur exists se place après une variable et retourne un booléen vrai ou faux pour dire si elle existe ou non.if (a exists) print(a) else print("a n'existe pas")L'opérateur defer se place avant toute une expression pour différer l'évaluation de l'expression.
y = defer x + 1, x = 1, print(y), // affiche 2 x = 2, print(y) // affiche 3
Expression
Les expressions permettent d'effectuer des calculs mathématiques et des opérations booléennes.Notation des variables
Une variable est identifiée de manière unique par son nom unique. Sous ce nom utilisé, l'affectation permet d'y stocker une valeur numérique, texte, booléenne, une liste ou bien des champs.Listes
Pour lire dans une liste, il suffit d'ajouter l'index de lecture dans la liste entre parenthèses après le nom. L'index de lecture va de 0 à MAX_INT.list = [ 1, 2, 3 ], print(list(0), list(1), list(2), list(3)),Les listes peuvent être multi-dimensionnelles. De manière interne, ce sont des listes de listes; ainsi, aucune liste n'est de longueur fixe. Les éléments d'une liste peuvent être de différents types.
m(0) = 1, m(1) = 2, m(2) = [3,4], m(3) = fun { output(m(0),m(1),m(2)) }, print(m(2),m(2,0),m(2,1)), print(m(2).type), print(m(3,0),m(3,1),m(3,2),m(3,2,0)), print(m(3).type)
L'expressionfun { }est une fonction anonyme et partage les variables locales.
Champs
Pour ajouter un champ dans une variable, il suffit d'intercaler un point (.) avant le champ. Notez que l'objet doit déjà être une liste de champ ou rien sinon une erreur de type s'affiche. Les champs peuvent se succéder indéfiniment.h = { a : 1, b : 2 }, h.c = 3, print(h.a, h.b, h.c), a.champ1.champ12.champ123 = "bonjour !", print(a.champ1.champ12.champ123)On peut combiner les champs et les listes afin de former un ensemble de données JSON que l'on pourra lire et modifier.
j = { a : 1, b : [ { c : 2 }, { d : [4,5,6] } ], e : 7 }, print(j.e,j.b(1).d), j.b(0).f = 8, print(j.b(0).f)On peut lire un champ via un texte. Par exemple, si
a = { b : 1, c : 2 }alors
a("b")est équivalent à
a.b.
Construire dynamiquement une liste
On peut créer dynamiquement une liste à l'aide d'un compteur.list = [], counter = 0, while(counter < 10) { list(counter) = (counter + 1) * 2, counter = counter + 1 }, list.length = 10, print(list@ToSequence(list, ","))
La fonction list@ToSequence est définie dans un fichier partagé. L'arobase (@) sépare l'espace de nom list du nom de la fonction ToSequence.
Notez qu'il faut spécifier la longueur de la liste lorsqu'elle est construite dynamiquement.
Type de données
Chaque élément de donnée a un type stocké dans le champ type. On accède à ce champ paru = 1, print(u.type). Les types définis par défaut sont:
- number
- constantString
- boolean
- list
- hash
- sandbox
list = [ 1, 2, 3 ], print(list.elements.length), print(list.elements(0))Un nombre ou un texte est stocké dans le champ 'value'. Vous pouvez accéder à ce champ.
a = "bonjour !", print(a.value), b = 1, print(b.value)
Accéder au champ 'value' d'un texte affiche le texte entre guillemets.
En fait, toutes les données sont des structures avec un champ 'type' et un champ 'value' ou un champ 'elements' selon le type de donnée. La machine virtuelle traite automatiquement chaque type de manière transparente.