Oxoscript se transforme en NanoPy - plus d'infos

Introduction

L’indentation et les constructions de base du langage, telles que if, while, for, def, ont été principalement reprises de Python. NanoPy contient de nombreuses simplifications, appelées “sucre syntaxique”, qui lui permettent de ressembler au code Python malgré les différences conceptuelles. Par exemple, dans NanoPy, chaque variable devrait être initialisée avec un type avant de pouvoir être utilisée. Grâce aux extensions, la première affectation d’une valeur de variable détermine son type. On ne doit le préciser que si celui-ci ne peut pas être déterminé automatiquement. Ainsi, dans de nombreux cas, l’instruction est identique au code Python.

a = 10
b = 3.145
c = true
d = [1,2,3,4]

Si le type ne peut pas être déterminé, il est ajouté par deux points :

a:long
b:float[5]

Cette forme existe aussi en Python, mais elle y est optionnelle, car les variables n’ont pas de type fixe. Cela rend Python plus flexible, mais présente des inconvénients, entre autres des problèmes de gestion de la mémoire qui doivent être résolus avec des concepts supplémentaires (carboge collection, etc.). Dans NanoPy, la gestion de la mémoire est fixe. Nous réduisons ainsi considérablement la consommation de mémoire et évitons en outre la fragmentation de la mémoire.

NanoPy connaît les types suivants : byte, int, long, float et bool. Le langage connaît également la notion de classe, qui permet de créer des types de données abstraits. Un type de données abstrait est une classe de variables composée de plusieurs types élémentaires :

class Rectangle:
  left:int
  top:int
  width:int
  height:int

Lors de la création de variables, il est désormais possible d’utiliser la nouvelle classe.

a:Rectangle

Outre les objets individuels, il est également possible de créer des listes d’objets en écrivant le nombre souhaité entre crochets :

liste:Rectangle[10]

Les différents éléments peuvent ensuite être lus/écrits via l’index entre crochets :

liste[1].left = 10
pixels = liste[1].width * liste[1].height

Il est également possible d’initialiser directement une variable d’une classe. L’exemple suivant crée un objet de la classe Rectangle et définit immédiatement des valeurs pour left et top.

c = Rectangle(left=10,top=50)

L’initialisation de plusieurs valeurs d’un objet peut également être appliquée aux listes:

liste[0](left=10,top=50)

Il est important de noter que le langage n’a pas de gestion dynamique de la mémoire. C’est pourquoi il n’y a pas de new, comme dans d’autres langages, pour créer un objet. Toutes les variables sont créées par déclaration statique et sont conservées tant que le programme, ou la fonction concernée, est en cours d’exécution. Il en résulte une vitesse d’exécution très optimisée en termes de mémoire et de rapidité.

Comme il est d’usage en Python, il est également possible de déclarer des boucles et des conditions dans NanoPy. Celles-ci se présentent de manière très similaire. Les conditions avec if, else, respectivement elif ont même l’air identiques :

if a>b:
  print("a is greater than b")
elif a==b:
  print("both numbers are equal")
else:
  print("b is greater than a")

L’indentation est centrale et résolue de manière identique à celle de Python. On voit aussi dans l’exemple que les deux points ont été repris pour rester compatibles.

Comme l’ont montré les tests, ce sont surtout les débutants qui ont des difficultés avec les concepts syntaxiques d’un langage de programmation, Python étant déjà nettement plus simple que Java ou C/C++ par exemple. Mais cela pourrait être encore plus simple si l’on pense aux langages de programmation pédagogiques des débuts, malheureusement oubliés aujourd’hui. En Basic (“Beginner’s All-purpose Symbolic Instruction Code), par exemple, les deux points ne sont pas nécessaires. Pour les appels de fonction sans valeur de retour, les parenthèses sont également inutiles et pourraient être supprimées.

Cette forme de saisie est également possible dans NanoPy et peut donc être utilisée :

if a>b
  print "a is greater than b"
elif a==b
  print "both numbers are equal"
else
  print "b is greater than a"

Les deux points peuvent également être supprimés pour class, def, for et while. On peut donc aussi bien écrire de manière compatible avec Python que laisser tomber les constructions inutiles. Les deux sont valables et possibles.

Il y a deux types de boucles à utiliser, la boucle for et la boucle while.

Pour la boucle for, la syntaxe est légèrement différente de celle de Python :

for i in 10:
  print(i)

Cette instruction compte de 0 à 9, c’est-à-dire qu’elle est parcourue dix fois. Il est également possible d’indiquer une variable au lieu d’un nombre. Le nombre détermine le nombre de passages, qui commence toujours à 0.

L’instruction suivante montre comment parcourir une plage de valeurs donnée :

for i in [5..10]:
  print(i)

Les nombres sont inclus, c’est-à-dire qu’ils sont comptés de 5 à 10 (inclus). On peut aussi compter à rebours si le deuxième nombre est plus petit que le premier.

Les listes peuvent également être itérées avec des boucles. Il faut noter ici que l’élément de la liste est à chaque fois une copie de l’entrée originale. Si l’on souhaite modifier un élément de la liste, il faut adresser les éléments via l’index :

list = [1,2,3,4,5,6,7,8,9,10]
for i in list
  print i

for i in sizeof(list)
  list[i] = list[i] + 1

for i in list
  print i

L’instruction correspondante en Python est plus flexible, mais aussi plus difficile à comprendre pour les débutants et donc non possible dans NanoPy :

for i in range(0,10):
  print(i)

Ces instructions peuvent également être écrites sans deux points ni parenthèses :

for i in 10
  print i
for i in [5..10]
  print i

Les instructions individuelles peuvent également être exécutées sur une ligne :

for i in 10 print i
for i in [5..10] print i

La parenthèse dans les appels de fonction peut être supprimée, à condition que les parenthèses qui suivent ne soient pas utilisées dans les fonctions. L’exemple suivant conduit à une erreur de programme

print (1+2)*3

Dans ce cas, l’expression entière doit être mise entre parenthèses:

print ((1+2)*3)

L’exemple suivant d’une boucle while montre comment quelque chose peut être répété aussi longtemps qu’une certaine condition est remplie:

i = 0
while i < 10
  print i
  i=i-1

Les chaînes de caractères / strings sont représentées dans NanoPy sous forme de tableaux d’octets, un octet étant utilisé par caractère. L’Unicode n’est pas possible pour le moment, car il utilise trop de mémoire. Lorsqu’une chaîne de caractères est attribuée à une variable, un tableau d’octets est automatiquement créé (:byte[114]). La longueur maximale dépend du système et peut être demandée avec sizeof(liste). Si l’on souhaite une autre taille, on peut la déclarer explicitement.

a = "C'est un test"
print sizeof(a)
b:byte[40]
b = "Bonjour"

Lorsqu’une valeur numérique est attribuée à un tableau d’octets, elle est automatiquement convertie en une chaîne de caractères :

i = 10
a = "Mon chiffre porte-bonheur est " + i
print a

Les fonctions sont déclarées avec def, comme en Python, et peuvent être déclarées avec ou sans paramètres. Si aucun type n’est indiqué pour les paramètres, le type int est pris en compte.

def helloWorld():
  print("Hello world")

def summe(a,b)->int:
  return a+b

def floatsumme(a:float,b:float)->float:
  return a+b

Les types de paramètres et de retours sont repris de Python. Là, ils sont facultatifs, ici ils sont obligatoires, à l’exception des paramètres int.

Les fonctions sans paramètres et sans valeurs de retour peuvent être écrites sous une forme abrégée. Voici les mêmes exemples sous forme simplifiée :

def helloWorld
  print "Hello world"

def summe(a,b)->int
  return a+b

def floatsumme(a:float,b:float)->float
  return a+b

La forme abrégée sans parenthèses n’est autorisée que si aucun paramètre ne doit être déclaré.

Les classes peuvent également être imbriquées. En outre, il est également possible d’ajouter des fonctions aux classes.

class Shape:
  position:vector
  radius:int

  def init
    position = vector(x=120,y=120)
    radius = 100

	def draw
    clear
    drawCircle position.x,position.y,radius
    update

myShape:Shape
myShape.init
myShape.draw

En plus de Python, le langage connaît également des constantes. Comme les variables, ce sont des espaces réservés aux valeurs, mais leur valeur ne peut pas être modifiée en cours d’exécution. Les constantes peuvent également être calculées et on peut les utiliser partout où l’on peut utiliser des variables. De plus, elles peuvent également être utilisées pour des déclarations statiques, par exemple des listes. Pour la déclaration, nous utilisons l’instruction const :

const WIDTH = 10
const HEIGHT = 20
const AREA = WIDTH * HEIGHT

elements:vector[AREA*2]

La prudence est de mise lors du passage de valeurs à des fonctions. Le langage copie toutes les valeurs, c’est-à-dire que lorsqu’une liste ou un objet est transmis à une fonction, une copie est créée. Cette manipulation s’appelle by value. D’autres langages permettent de passer des références à des variables (by reference). Ceci n’est pas possible avec NanoPy, mais peut être facilement contourné.

Les grands objets ou les listes qui sont universels sont simplement déclarés globalement. Ces variables sont toujours visibles dans les fonctions et ne doivent pas être passées en paramètre :

list:byte[200]

def initializeList
  for i in sizeof(list)
    list[i] = 0

initializeList

Cela permet d’économiser une mémoire précieuse et d’augmenter la vitesse d’exécution du code, car il n’est pas nécessaire de copier de gros objets.

Les variables de classe, c’est-à-dire les variables définies dans une déclaration de classe, sont globales à l’objet, c’est-à-dire que toutes les fonctions de l’objet peuvent également accéder à ces variables.

class Rectangle
  pos:vector
  size:int

  def init
    pos.x = 10
    pos.y = 20
    size = 30

r:Rectangle
r.init

Dans la communication, des données sont souvent échangées entre des processus, des coprocesseurs ou d’autres systèmes. NanoPy met à disposition une commande de conversion qui permet de convertir des données d’objets en tableaux d’octets. L’instruction « peut être utilisée à cet effet :

L’instruction suivante convertit l’objet v en un tableau d’octets buf. Comme vector contient deux variables float de 4 octets chacune, buf a une longueur de 8 octets.

v = vector(x=10,y=20)
buf << v

Inversement, il est possible de reconstruire une structure à partir d’une mémoire tampon :

buf = [0,0,32,65,0,0,160,65]
v:vector
v << buf
print v.toString()

Si des fonctions doivent être transmises à des listes, le dimensionnement ne doit pas être indiqué explicitement.

def sumfloats(list:float[])->float:
  result:float

  for v in list:
    result = result + v

  return result

floats:float[5] = [0.1,0.2,0.3,0.4,0.5]

print(sumfloats(floats))

Forme simplifiée (pas de deux points, une seule ligne, les parenthèses non obligatoires ont été supprimées) :

def sumfloats(list:float[])->float
  result:float
  for v in list result = result + v
  return result

floats:float[5] = [0.1,0.2,0.3,0.4,0.5]

print sumfloats(floats)

La dimension peut également être laissée ouverte lors des restitutions de fonctions :

def test(t)->byte[]
  a:byte[20]
  a="byte[20]"

  b:byte[10]
  b="byte[10]"

  if t%2 == 0
    return a
  else
    return b

for i in 10
  print test(i)

Le langage NanoPy est utilisé pour plusieurs systèmes, c’est pourquoi cet article n’aborde pas les fonctions concrètes spécifiques aux plateformes. L’objectif est de rendre tous les produits Oxon “programmables” à l’aide de ce langage, ce qui sera réalisé progressivement au cours des prochains mois. Actuellement, NanoPy est exclusivement utilisable sur les ordinateurs d’apprentissage pédagogiques de la série Oxocard-Mini. Les cartes Oxocard Blockly, qui sont principalement programmées avec Blockly, contiennent une version préliminaire simplifiée d’NanoPy qui n’est plus développée et qui n’est pas compatible avec la version actuelle.

Sur les Oxocard Minis, mais aussi sur les futurs appareils, nous mettons à disposition dans NanoPy un modèle de programmation piloté par des événements. Sur les Oxocards, il est déjà possible de l’utiliser et de l’expérimenter. NanoPy met à disposition des procédures d’événements qui sont appelées par le système d’exploitation. Sur les Oxocards, il s’agit des deux procédures onDraw et onClick. onDraw est appelé lorsque l’écran doit être redessiné. Selon la charge du système, cela se produit jusqu’à 50 fois par seconde. onClick est appelé lorsque l’utilisateur clique sur un bouton. Sur les appareils IoT, nous mettrons à disposition d’autres procédures d’événements qui permettront aux appareils de fonctionner facilement en économisant de l’énergie.

Cet exemple allume/éteint l’écran lorsqu’on appuie sur le bouton gauche/droit :

turnOn = false

def onDraw
  if turnOn
    background 255,255,255
  else
    background 0,0,0
  update

def onClick
  b = getButtons()
  if b.left
    turnOn = false
  elif b.right
    turnOn = true

Zusammenfassung