Berry est un langage de script embarqué à typage dynamique ultra-léger
(berry-lang.github.io)- Berry est un langage de script à typage dynamique destiné aux appareils embarqués peu puissants ; sur une base ARM Cortex M4, le cœur de l’interpréteur fait moins de 40 KiB et peut fonctionner avec moins de 4 KiB de heap
- Le runtime se compose d’un compilateur en un seul passage et d’une VM à registres ; l’ensemble du code est écrit en ANSI C99, ce qui facilite son intégration à du code embarqué existant
- Pour les performances,
int,real,booleanetstringne sont pas implémentés comme objets de classe, tandis que seulslist,mapetrangesont traités comme objets de classe - Les fonctionnalités du langage couvrent à la fois la programmation impérative, orientée objet et fonctionnelle, avec GC, FFI, gestion des exceptions, fichiers de bytecode et modules d’extension
- La configuration d’objets à la compilation et la fixation en flash permettent de réduire l’utilisation de la RAM pour le code, les classes et les modules, ce qui le rend adapté aux environnements à fortes contraintes mémoire
Un runtime pensé pour l’embarqué
- Berry est un langage de script à typage dynamique conçu pour des appareils embarqués peu performants, comme les microprocesseurs
- Avec un CPU ARM Cortex M4, l’ISA Thumb et le compilateur ARMCC, la taille du code du cœur de l’interpréteur est inférieure à 40 KiB
- Dans les mêmes conditions, il peut s’exécuter avec moins de 4 KiB de heap
- L’interpréteur est écrit en ANSI C99 et comprend un compilateur de bytecode en un seul passage ainsi qu’une machine virtuelle basée sur des registres
- La conception des types privilégie les performances d’exécution en distinguant types valeur et types objet
- Types qui ne sont pas des objets de classe :
int,real,boolean,string - Types qui sont des objets de classe :
list,map,range
- Types qui ne sont pas des objets de classe :
- La configuration d’objets à la compilation stocke la plupart des objets constants dans le segment de données de code en lecture seule, ce qui réduit l’utilisation de la RAM au démarrage de l’interpréteur
Fonctionnalités du langage et modèle d’exécution
- Les types de base incluent
nil,boolean,integer,real,string,class,instance,module,list,map,range - Les expressions et opérateurs prennent en charge l’affectation, les relations, la logique, l’arithmétique, les opérations bit à bit, les champs, les index, la concaténation de chaînes, l’opérateur conditionnel, les parenthèses et les bytes buffers
- Les structures de contrôle fournissent
if elif else end,while,for,break,continue - Les fonctionnalités de fonction incluent variables locales et portée de bloc, instruction de retour, définition de fonctions imbriquées, closures basées sur des upvalues, fonctions anonymes et expressions lambda
- Les classes ne prennent en charge que l’héritage simple public et fournissent surcharge de méthodes et d’opérateurs, méthodes constructeur et méthodes destructeur
- Le GC utilise un Mark-Sweep GC
- La gestion des exceptions permet de lancer n’importe quelle valeur d’exception avec l’instruction
raiseet prend en charge plusieurs modes de catch - Grâce aux fichiers de bytecode, il est possible d’exporter une fonction dans un fichier de bytecode, puis de la recharger pour l’exécuter
Modules, intégration C et exemples
- La gestion des modules prend en charge à la fois les modules intégrés et les modules d’extension
- Les modules d’extension incluent les modules script, les modules de fichiers bytecode et les modules de bibliothèques partagées comme
*.soet*.dll - Le code, les classes et les modules peuvent être fixés en flash pour réduire l’utilisation de la RAM
- Un support Regex optionnel et un mapping LVGL optionnel sont proposés
- Les modules d’extension incluent les modules script, les modules de fichiers bytecode et les modules de bibliothèques partagées comme
- Grâce à la Native C interface, il peut être embarqué comme une bibliothèque dans du code existant ; du code comme Tasmota est cité en exemple
- Un easy mapping optionnel est fourni pour appeler du code C depuis Berry
- Les exemples de syntaxe montrent l’itération sur une liste, la création d’une map et l’itération sur ses clés, la déclaration
classet l’héritage simple via l’opérateur: - D’autres exemples de code sont disponibles sur GitHub
1 commentaires
Commentaires sur Hacker News
Pour un runtime de 40 Ko, l’ensemble de fonctionnalités est étonnamment riche : un langage proche de Python/Ruby tourne dans une VM avec ramasse-miettes, et prend en charge les styles procédural, orienté objet et fonctionnel.
Même en le parcourant rapidement, il a l’air assez agréable à écrire. Ce qui se démarque particulièrement, c’est la possibilité de précréer des objets constants et de les placer en grande partie en ROM, en n’utilisant la RAM que pour les données réellement modifiables. À ma connaissance, MicroPython ou Lua n’ont pas cette approche, et sur des MCU où la ROM/le flash sont abondants mais où la RAM manque, cela fait une grosse différence.
Si un module contient des données stockées sous forme d’objets immuables comme des chaînes ou
bytes(), elles sont lues directement depuis le flash sans être d’abord copiées en RAM. En revanche, pour effectuer le freeze, il faut exécuter le code une fois sur un ordinateur de bureau.https://docs.micropython.org/en/latest/reference/constrained...
Le flux pratique consiste donc à déployer le code au runtime jusqu’à ce que la base de code soit stable ou proche de la phase de produit, puis à ce moment-là à freezer le code dans le firmware. Un modèle encore plus pratique est proposé avec la prise en charge de
mapfs: allouer une partie du flash au stockage du code, compiler Python en bytecode (mpy_cross), l’uploader dans cette zone et l’exécuter au runtime. La preuve de concept existe déjà, mais il reste des détails à régler avant l’intégration dans la branche principale.https://github.com/micropython/micropython/pull/8381
Voir les rotables dans https://eluaproject.net/doc/v0.9/en_arch_ltr.html
Berry est utilisé dans Tasmota : https://tasmota.github.io/docs/Berry/
Ça a vraiment l’air bien. Personnellement, je pense que c’est proche de la meilleure documentation que j’aie vue jusqu’ici.
J’aime particulièrement le « short manual », qui permet à un développeur expérimenté de se faire rapidement une idée du langage. Je ne connaissais pas Tasmota auparavant, mais je vais probablement chercher un prétexte pour l’essayer dans un projet.
Ce qui m’intéresse le plus, c’est de savoir comment il se compare à Lua en performances et en consommation mémoire, et jusqu’où le sandboxing est possible.
Je me demande aussi s’il peut exécuter du code non fiable.
Si ce langage constitue une alternative valable avec des structures simples et des constructeurs de base, je pourrais le recommander pour se débarrasser de scripts Python et de leurs dépendances.
Le sandboxing doit être pris en charge par l’application. WebAssembly est une bonne alternative pour le sandboxing.
asyncétait ajoutée, cela le distinguerait clairement de Lua pour les moteurs de jeu.Quelque chose de presque identique, mais avec moins de paradigmes et un typage statique, serait vraiment excellent comme langage de configuration.
Dans plusieurs projets nécessitant une configuration complexe, on a parfois permis d’utiliser Lua ou Starlark/Python au lieu de devoir écrire des centaines de lignes de YAML, et cela semblait nettement mieux. Mais l’autocomplétion et la réflexion manquent toujours. Je ne vois pas vraiment de candidat idéal pour cet usage, et presque tous les petits langages de script embarquables sont typés dynamiquement.
Cela dit, https://dhall-lang.org/ montre qu’on peut traiter beaucoup de configurations avec un typage statique, et que les avantages sont importants. Il semble pouvoir être intégré programmatiquement dans plusieurs langages.
https://docs.dhall-lang.org/howtos/How-to-integrate-Dhall.ht...
Ça a l’air bien. Même si, pour l’instant, je reste assez attiré par uLisp (http://www.ulisp.com), je suis content de voir ce genre de choses apparaître.
Si vous avez tendance à regarder d’abord des exemples de code pour vous faire une idée d’un langage, le répertoire d’exemples vaut le détour : https://github.com/berry-lang/berry/tree/master/examples
Ce serait très bien de proposer des bindings pour d’autres langages. Nous utilisons quickjs depuis Rust et cela fonctionne plutôt bien.
Comme on peut n’exposer à la VM que ce que l’on veut, il est aussi possible d’exécuter du code non fiable.
Cela ressemble à un langage optimisé pour l’embarqué. La conception et la documentation sont bonnes, et il ne semble pas avoir de comportements étranges ou inattendus.
La syntaxe est aussi agréablement minimale et correspond à mes goûts. Je le garderai sûrement en tête pour mon prochain projet ESP32.
Il semble vraiment bien adapté à cette niche, et j’aime surtout le fait qu’il fasse des choses pour lesquelles aucun équivalent évident dans d’autres langages ne me vient à l’esprit.
Je me demande s’il existe une approche standard pour afficher le nom des fonctions de script dans les frames d’une stack trace native.
En cas de crash ou lors de l’utilisation d’un profileur CPU, j’aimerais savoir s’il est possible d’afficher ensemble la stack trace native et la stack trace du script.