Le langage d'Aseba

Le langage d'Aseba

Cette 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 des version 1.2 et 1.3 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 à une classe populaire de langages, incluant Pascal et Matlab (un langage de programmation scientifique commun) par exemple. Nous nous attendons à ce que cette ressemblance permette aux développeurs ayant des connaissances préalables de ces langages de se sentir à l'aise et d'apprendre rapidement. Sémantiquement, Aseba 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. Les valeurs sont comprises entre -32768 et 32767, ce qui correspond à la plage des nombres entiers signés sur 16 bits.

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écimaux 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

En notation binaire, les valeurs sont comprises entre 0b0000000000000000 et 0b1111111111111111, alors qu'en hexadécimal elles le sont entre 0x0 et 0xffff. Les valeurs qui seraient plus grandes que 32767 en décimal sont interprétées comme nombres négatifs.

Variables

Les variables font référence soit à des valeurs scalaires, soit à des tableaux de valeurs scalaires. Les variables utilisateurs doivent être déclarées au début du programme 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 :

  • Le nom fait la différence entre majuscules et minuscules : une variable appelée "foo" est différente d'une autre appelée "Foo"
  • Le nom doit commencer par un caractère alphabétique valide, ou '_'
  • Le nom ne peut contenir que des caractères alphanumériques, '_' ou '.'
  • Le nom ne peut pas être identique à un des mots clés d'Aseba (voir mots clés réservés)

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 nœuds 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. Par convention, les constantes sont souvent écrites en majuscule.

# 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 :

  • On accède à un seul élément en utilisant l'opérateur crochet encadrant une seule valeur. Les index d'un tableau commencent à zéro. N'importe quelle expression peut être utilisée comme index, incluant des expressions mathématiques impliquant d'autres variables.
  • On peut accéder à une plage de valeurs en utilisant l'opérateur crochet encadrant deux expressions constantes séparées par deux-points ':'. La validité de la plage est vérifiée à la compilation.
  • En l'absence de l'opérateur crochet, on accède au tableau dans son ensemble.

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écédence 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
[] Indice dans un tableau Unaire
- Moins unaire Unaire
~ NON binaire Unaire
abs Valeur absolue Unaire
2 * / Multiplication, division Binaire
% Modulo Binaire
3 + - Addition, soustraction Binaire
4 << >> Décalage à gauche, à droite Binaire
5 & ET binaire Associatif à gauche Binaire
6 ^ OU exclusif binaire Associatif à gauche Binaire
7 | OU binaire Associatif à gauche Binaire
8 == != < <= > >= Condition † Binaire
9 not NON logique Unaire
10 and ET logique Binaire
11 or OU logique Binaire
12 = 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

Remarque
† Seulement possible dans la condition es structures if, when, et while
‡ Seulement possible sur une ligne seule, comme a-- ou a[i]++, et pas à l'intérieur d'une expression

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 :

  • Du côté droit d'une affectation
  • Comme index lors de l'accès à un tableau
  • A l'intérieur d'appels de fonctions
  • Comme argument lors de l'émission d'un évènement

Contrôle de flux

Conditions

Aseba fournit deux types de déclarations conditionnelles : déclarations if et déclarations when. Une déclaration conditionnelle consiste en une expression conditionnelle et des blocs de code.

Les expressions conditionnelles sont formées à partir d'un opérateur de comparaison et de deux opérandes qui sont des expressions arithmétiques ; par exemple a < b+3 est une expression conditionnelle. 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

Une expression conditionnelle peut aussi être formée en combinant des expressions de comparaison avec les opérateurs logiques and (et, conjonction logique), or (ou, disjonction logique), not (non, négation logique) ; par exemple, (a < b+3) or (a < 0). La précédence peut être contrôlée par des parenthèses ; par exemple ((a < b) or (b < c)) and ((d < e) or (e < f)). Bien que le langage Aseba ne fournit pas de variables ou de littéraux booléens — vous ne pouvez pas écrire flag = true ou if flag then — le résultat d'une comparaison est considéré comme une variable booléenne (vraie ou fausse) qui peut être utilisée avec les opérateurs logiques. Les expressions conditionnelles sont aussi utilisées dans les déclarations while (voir section boucles).

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 l'évaluation précédente 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 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 programme est exécuté quand ce dernier est chargé ou au reset.

Afin de permettre l'exécution du code lors de la réception d'un nouvel événement, le programme 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 programme 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

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

Initialisation

Les instructions placées entre la déclaration des variables et les gestionnaires de sous-routines et d'événements sont exécutées lorsque le programme est initialisé :

var état

état = 0
call leds.bottom.left(0,0,32)
call leds.bottom.right(0,32,0)
call leds.top(32,0,0)

Alors que l'initialisation de état aurait pu être effectuée lors de sa déclaration, l'initialisation des LEDs doit être faite par des instructions.

Lors de la programmation d'un robot, vous aurez probablement envie de définir un événement qui ré-initialise l'état du robot. C'est possible en écrivant des instructions à l'intérieur d'une sous-routine et en appelant cette sous-routine depuis le gestionnaire d'événement. Il est aussi possible d’appeler la sous-routine lors de l'initialisation du programme, bien qu'elle n'ait pas encore été déclarée :

var état

callsub init  # initialisation du programme

# sous-routine pour l'initialisation
sub init
    état = 0
    call leds.bottom.left(0,0,32)
    call leds.bottom.right(0,32,0)
    call leds.top(32,0,0)

# ré-initialisation lorsque le bouton central est touché
onevent button.center
    callsub init

Envoi des événements externes

Un programme 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 programme de déclencher l'exécution de code sur un autre nœud ou de communiquer avec un programme externe.

onevent ir_sensors
emit valeurs_des_capteurs valeurs_des_capteurs_de_proximite

Fonctions natives

Nous avons conçu le langage de programme 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ûres, 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)

Que lire ensuite ?

La page suivante peut vous intéresser :

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License