Le langage d'Aseba

Cet aide est aussi disponible dans Aseba Studio dans le menu Aide -> Langage. Lorsque nous utilisons des mots techniques, nous les lions vers l'article Wikipedia correspondant. De plus, la page concepts explique quelques concepts importants liés à Aseba. Cette page présente le langage de la version 1.2 d'Aseba, pour des versions précédentes, veuillez lire cette page.

Le langage de programmation d'Aseba permet de programmer le comportement des nœuds Aseba. Syntaxiquement, ce langage ressemble à Matlab (un langage de programmation scientifique commun) : cette ressemblance permet aux développeurs ayant des connaissances préalables sur les langages de scripts de se sentir à l'aise et d'apprendre rapidement. Sémantiquement, c'est un langage de programmation impératif avec un seul type simple (nombres entiers signés sur 16 bits) et des tableaux. Cette simplicité permet aux développeurs sans connaissances préalables d'un système de type de programmer des comportement ; les nombres entiers étant le type de variable le plus naturel. De plus, les nombres entiers sont particulièrement adaptés aux robots à micro-contrôleurs.

Commentaires

Les commentaires permettent d'ajouter des informations qui seront ignorées par le compilateur. Les commentaires sont très utiles pour annoter votre code avec des informations compréhensibles par un humain ou pour désactiver certaines parties du code. Les commentaires commencent avec un # et finissent à la fin de la ligne.

Exemple :

# ceci est un commentaire
var b    # un autre commentaire

Les commentaires sur plusieurs lignes sont également possibles. Ils commencent par #* et finissent par *#.

Exemple :

#*
ceci est un commentaire
sur plusieurs lignes
*#
var b    # commentaire simple

Scalaires

Les valeurs scalaires sont utilisées dans Aseba pour représenter des nombres. Elles peuvent être utilisées dans n'importe quelle expression, telle que l'initialisation d'une variable, dans une expression mathématique, ou dans une condition logique.

Notation

Les scalaires peuvent être écrit en utilisant plusieurs bases différentes. La façon la plus naturelle correspond au système décimal, en utilisant les chiffres de 0 à 9. Les nombres négatifs sont déclarés en utilisant un signe - (moins) devant le nombre.

i = 42
i = 31415
i = -7

Les nombres binaires et hexadécimales peuvent également être utilisés. Les nombres binaires sont préfixés par 0b, alors que les nombres hexadécimales sont préfixés par 0x.

# notation binaire
i = 0b110         # i = 6
i = 0b11111111    # i = 255

# notation hexadécimale
i = 0x10    # i = 16
i = 0xff    # i = 255

Variables

Les variables font référence soit à des valeurs scalaires, soit à des tableaux de valeurs scalaires. Les valeurs sont comprises entre -32768 et 32767, ce qui correspond à la plage des nombres entiers signés sur 16 bits. Les variables utilisateurs doivent être déclarées au début du script Aseba, en utilisant le mot clé var, avant tout autre opération.

Le nom d'une variable doit suivre un certain nombre de règles :

Les variables peuvent être initialisées durant la déclaration, en utilisant le symbole d'assignation combiné à n'importe quelle expression mathématique. Une variable qui n'a pas été initialisée au préalable aura une valeur aléatoire, rien ne garantit qu'elle sera égale à zéro.

Exemple :

var a
var b = 0
var c = 2*a + b # attention: 'a' n'est pas initialisée

Mot clés réservés

Les mots clés suivants ne peuvent pas être utilisés pour nommer des variables, car ils sont déjà utilisés par la langage Aseba.

Mots clés
abs call callsub do
else elseif emit end
for if in onevent
return step sub then
var when while

Constantes

Des constantes peuvent être définies dans Aseba Studio, en utilisant le panneau "Constantes", car elles ne peuvent pas être définies directement dans le code. Tout comme une variable normale, une constante contient une valeur numérique, qui peut être utilisée plusieurs fois dans le programme. Mais contrairement aux variables, une constante ne peut pas être modifiée durant l'exécution. Les constantes sont utiles quand on veut facilement changer le comportement d'un programme d'une exécution à l'autre, comme le changement d'une valeur de seuil. Une constante a une portée couvrant l'ensemble des noeuds Aseba. Une constante ne peut pas avoir le même nom qu'une variable, une erreur est produite dans le cas contraire. Les constantes se comportent exactement comme des valeurs numériques.

# en supposant une constante appelée SEUIL
var i = 600

if i > SEUIL then
    i = SEUIL - 1
end

Tableaux

Un tableau représente une zone contiguë de mémoire, manipulée comme une seule entité logique. La taille d'une tableau est fixe, et elle doit être définie au moment de la déclaration. On peut déclarer des tableaux en utilisant l'opérateur crochet habituel []. Le nombre entre les crochets déclare le nombre d'éléments attribués au tableau, c'est-à-dire la taille du tableau. Il peut s'agir de n'importe quelle expression constante, incluant des opérations mathématiques portants sur des scalaires et des constantes. Une initialisation optionnelle peut avoir lieu, en utilisant le constructeur de tableau (voir ci-dessous). Dans le cas où une initialisation est effectuée, la taille du tableau peut être omise.

Exemple:

var a[10]                   # tableau de 10 éléments
var b[3] = [2, 3, 4]        # initialisation
var c[] = [3, 1, 4, 1, 5]   # taille implicite de 5 éléments
var d[3*FOO-1]              # taille déclarée en utilisant une expression constante (FOO déclarée en tant que constante)

Les tableaux peuvent être accédés selon plusieurs syntaxes :

Exemple :

var foo[5] = [1,2,3,4,5]
var i = 1
var a
var b[3]
var c[5]
var d[5]

a = foo[0]        # copie le 1er élément depuis 'foo' vers 'a'
a = foo[2*i-2]    # identique
b = foo[1:3]      # prends le 2ème, 3ème et 4ème éléments de 'foo', copie vers 'b'
b = foo[1:2*2-1]  # identique
c = foo           # copie les 5 éléments de 'foo' vers 'c'
d = c * foo       # multiplie élément par élément les tableaux 'c' et 'foo', copie vers 'd'

Constructeur de tableau

Un constructeur de tableau permet de construire un tableau à partir de variables, d'autres tableaux, de scalaires, ou même n'importe quelle expression. Ils sont utiles dans plusieurs cas, par exemple lors de l'initialisation d'un autre tableau, ou comme opérande dans des expressions, fonctions ou événements. Un constructeur de tableau est déclaré en utilisant des crochets entourant plusieurs expressions séparées par une , (virgule). La taille d'un constructeur de tableau est la somme de la taille des éléments le constituant, et elle doit correspondre à la taille du tableau servant à stocker le résultat.

Exemple :

var a[5] = [1,2,3,4,5]    # constructeur de tableau pour initialiser un tableau
var b[3] = [a[1:2],0]    # contenu du tableau 'b' : [2,3,0]
a = a + [1,1,1,1,1]    # ajoute 1 à chaque élément du tableau 'a'
a = [b[1]+2,a[0:3]]    # le résultat est [3,2,3,4,5]

Expressions et assignations

Les expressions permettent les calculs mathématiques et sont écrites en syntaxe mathématique infixée commune. L'assignation utilise le mot clé = et affecte le résultat du calcul d'une expression à une variable scalaire, à l'élément d'un tableau, ou dans tout ou partie d'un tableau. Aseba fournit plusieurs opérateurs. Le tableau ci-dessous fournit une brève description, ainsi que la préséance de chaque opérateur. Pour évaluer une expression dans un ordre différent, nous pouvons utiliser une paire de parenthèses afin de grouper une sous-expression.

Préséance Opérateur Description Associativité Arité
1 () Groupe une sous-expression Unaire
2 * / Multiplication, division Binaire
% Modulo Binaire
3 + - Addition, soustraction Binaire
4 << >> Décalage à gauche, à droite Binaire
5 | OU binaire Associatif à gauche Binaire
6 ^ OU exclusif binaire Associatif à gauche Binaire
7 & ET binaire Associatif à gauche Binaire
8 - Moins unaire Unaire
9 ~ NON binaire Unaire
10 abs Valeur absolue Unaire
11 = Assignation Binaire
|= ^= &= Assignation avec un OU, OU exclusif, ET binaire Binaire
*= /= Assignation avec un produit ou quotient Binaire
%= Assignation avec un modulo Binaire
+= -= Assignation avec une somme ou différence Binaire
<<= >>= Assignation avec un décalage à gauche / droite Binaire
++ -- Incrément, décrément unaire Unaire

La version assignation avec des opérateurs binaires consiste à appliquer l'opérateur à une variable et à stocker le résultat dans cette même variable. Par exemple A *= 2 est équivalent à A = A * 2. Ces raccourcis ont pour but de rendre le code plus lisible.

Exemple :

a = 1 + 1
# Résultat : a = 2
a *= 3
# Résultat : a = 6
a++
# Résultat : a = 7

b = b + d[0]
b = (a - 7) % 5
c[a] = d[a]
c[0:1] = d[2:3] * [3,2]

Utilisation

Les expressions mathématiques sont un outil qui peut être appliqué dans une grande variété de situations. Voici quelques utilisations :

Contrôle de flux

Conditions

Aseba fournit deux types de conditions : if et when. Elles consistent en un test de conditions et des blocs de code. Le test consiste en des comparaisons éventuellement groupées en utilisant les opérateurs and (conjonction logique), or (disjonction logique), not (négation logique) et les parenthèses. Une comparaison consiste en un opérateur et deux opérandes, et peut être soit vraie soit fausse. L’opérande peut être une expression quelconque. La table suivante liste les opérateurs de comparaison.

Opérateur Valeur de vérité
== vrai si les opérandes sont égaux
!= vrai si les opérandes sont différents
> vrai si le premier opérande est strictement plus grand que le second
>= vrai si le premier opérande est plus grand ou égal au second
< vrai si le premier opérande est strictement plus petit que le second
<= vrai si le premier opérande est plus petit ou égal au second

Tant if que when exécutent un bloc de code différent selon qu'une condition est vraie ou fausse ; mais when exécute le bloc correspondant à vrai seulement si la dernière évaluation de la condition était fausse et que l'évaluation courante est vraie. Cette différentiation permet l'exécution de code seulement quand quelque chose change. Le if exécute un premier bloc de code si la condition est vrai, un second bloc à exécuter si la condition est fausse peut être ajouté par le mot-clé else. De plus, d'autres conditions peuvent être chaînées grâce au mot-clé elseif.

Exemple :

if a - b > c[0] then
    c[0] = a
elseif a > 0 then
    b = a
else
    b = 0
end

if a < 2 and a > 2 then
    b = 1
else
    b = 0
end

when a > b do
    leds[0] = 1
end

Ici le bloc when s'exécute seulement quand a devient plus grand que b.

Boucles

Deux constructions permettent la création de boucles : while et for.

Une boucle while exécute un bloc de code plusieurs fois tant qu'une condition est vraie. La condition est de même type que celle utilisée par if.

Exemple:

while i < 10 do
    v = v + i * i
    i = i + 1
end

Une boucle for fait itérer une variable sur un intervalle d'entiers, avec en option une taille de pas.

Exemple:

for i in 1:10 do
    v = v + i * i
end
for i in 30:1 step -3 do
    v = v - i * i
end

Blocs

Sous-routines

Lorsque vous effectuez une même suite d'opérations à plusieurs endroits dans le code, il est bon de mettre le code commun une seule fois dans une sous-routine, puis d'appeler cette dernière depuis les divers endroits. Vous pouvez définir une sous-routine en utilisant le mot-clé sub suivi du nom de la sous-routine. Vous pouvez appeler la sous-routine en utilisant le mot-clé callsub suivi du nom de la sous-routine. Les sous-routines doivent être définies avant leur utilisation. Les sous-routines ne peuvent pas avoir d'arguments, ni être récursives, que ce soit directement ou indirectement. Les sous-routines peuvent accéder à toutes les variables.

Exemple:

var v = 0

sub toto
v = 1

onevent test
callsub toto

Événements

Aseba a une architecture par programmation événementielle, ce qui signifie que des événements peuvent déclencher l'exécution de code de façon asynchrone. Les événements peuvent être externes, par exemple un événement défini par l'utilisateur venant d'un autre nœud Aseba, ou internes, par exemple émis par un capteur qui possède un valeur nouvellement acquise. La réception d'un événement exécute, si défini, le bloc de code commençant par le mot clé onevent suivi du nom de l'événement. Le code au début du script est exécuté quand ce dernier est chargé ou au reset. Un script peut aussi envoyer des événements en utilisant le mot clé emit, suivi par le nom de l'événement ainsi que du nom de la variable à envoyer, s'il y a lieu. Si une variable est fournie, la taille de l'événement doit correspondre à celle de l'argument à envoyer. En lieu et place d'une variable, des constructeurs de tableau et des expressions mathématiques peuvent aussi être utilisées pour les situations plus complexes. Les événements permettent ainsi à un script de déclencher l'exécution de code sur un autre nœud ou de communiquer avec un programme externe.

Afin de permettre l'exécution du code lors de la réception d'un nouvel événement, le script ne doit pas bloquer et donc ne pas contenir de boucle infinie. Par exemple dans le contexte de la robotique, où un programme de contrôle de robot traditionnel travaillerait à l'intérieur d'une boucle infinie, un script Aseba ferait simplement le travail lors d'un événement lié aux capteurs.

Exemple :

var run = 0

onevent start
run = 1

onevent stop
run = 0

onevent ir_sensors
if run == 1 then
    emit valeurs_des_capteurs valeurs_des_capteurs_de_proximite
end

Instruction return

Il est possible de sortir prématurément d'une sous-routine et de stopper l'exécution d'un événement en utilisant le mot clé return.

Exemple :

var v = 0

sub toto
if v == 0 then
    return
end
v = 1

onevent test
callsub toto
return
v = 2

Fonctions natives

Nous avons conçu le langage de script d'Aseba simple afin de permettre une compréhension aisée par des développeurs novices et pour implémenter la machine virtuelle efficacement. Pour implémenter des calculs complexes ou lourds, nous fournissons des fonctions natives implémentées en code natif pour une exécution rapide. Par exemple, une fonction native est la solution de choix pour effectuer un calcul de produit scalaire.

Les fonctions natives sont sûrs, dans le sens qu'elles spécifient et vérifient les tailles de leurs arguments, qui peuvent être des constantes, des variables, des accès à des tableaux, des constructeurs de tableau et des expressions. Dans le cas d'un tableau, nous pouvons accéder à tout le tableau, à un élément discret ou à un sous-intervalle du tableau. Les fonctions natives prennent leurs arguments par référence et peuvent modifier leurs contenus, mais ne retournent aucune valeur. Les fonctions natives sont appelées par le mot-clé call.

Exemple:

var a[3] = 1, 2, 3
var b[3] = 2, 3, 4
var c[5] = 5, 10, 15
var d
call math.dot(d, a, b, 3)
call math.dot(d, a, c[0:2], 3)
call math.dot(a[0], c[0:2], 3)