4 pontos por GN⁺ 2023-10-09 | 1 comentários | Compartilhar no WhatsApp
  • Berry é uma linguagem de scripting de tipagem dinâmica para dispositivos embarcados de baixo desempenho; no ARM Cortex M4, o núcleo do interpretador tem menos de 40 KiB e consegue rodar com menos de 4 KiB de heap
  • O runtime é composto por um compilador de passagem única e uma VM baseada em registradores, e todo o código é escrito em ANSI C99, facilitando a integração a código embarcado existente
  • Por desempenho, int, real, boolean e string não são transformados em objetos de classe; apenas list, map e range são tratados como objetos de classe
  • Os recursos da linguagem dão suporte a programação imperativa, orientada a objetos e funcional, e incluem GC, FFI, tratamento de exceções, arquivos de bytecode e módulos de extensão
  • Com composição de objetos em tempo de compilação e fixação em flash, é possível reduzir o uso de RAM de código, classes e módulos, tornando-a adequada a ambientes com fortes restrições de memória

Runtime voltado a ambientes embarcados

  • Berry é uma linguagem de scripting de tipagem dinâmica feita para uso em dispositivos embarcados de baixo desempenho, como microprocessadores
    • Em uma CPU ARM Cortex M4, Thumb ISA e compilador ARMCC, o tamanho do código do núcleo do interpretador fica abaixo de 40 KiB
    • Nas mesmas condições, ela consegue rodar com menos de 4 KiB de heap
  • O interpretador é escrito em ANSI C99 e inclui um compilador de bytecode de passagem única e uma máquina virtual baseada em registradores
  • O desenho dos tipos prioriza o desempenho de execução, distinguindo tipos de valor e tipos de objeto
    • Tipos que não são objetos de classe: int, real, boolean, string
    • Tipos que são objetos de classe: list, map, range
  • A composição de objetos em tempo de compilação armazena a maioria dos objetos constantes no segmento de dados de código somente leitura, reduzindo o uso de RAM ao iniciar o interpretador

Recursos da linguagem e modelo de execução

  • Os tipos básicos incluem nil, boolean, integer, real, string, class, instance, module, list, map e range
  • Expressões e operadores dão suporte a atribuição, relacionais, lógicos, aritméticos, bit a bit, campos, subscritos, concatenação de strings, operador condicional, parênteses e bytes buffer
  • As estruturas de controle oferecem if elif else end, while, for, break e continue
  • Os recursos de função incluem variáveis locais e escopo de bloco, instrução de retorno, definição de funções aninhadas, closures baseados em Upvalue, funções anônimas e expressões lambda
  • Classes dão suporte apenas a herança única pública e oferecem métodos, sobrecarga de operadores, métodos construtores e métodos destrutores
  • O GC usa Mark-Sweep GC
  • O tratamento de exceções permite lançar valores de exceção arbitrários com a instrução raise e oferece vários modos de catch
  • O recurso de arquivo de bytecode permite exportar funções para um arquivo de bytecode e depois carregá-las novamente para execução

Módulos, integração com C e exemplos

  • O gerenciamento de módulos oferece suporte tanto a módulos embutidos quanto a módulos de extensão
    • Módulos de extensão incluem módulos de script, módulos de arquivo de bytecode e módulos de biblioteca compartilhada como *.so e *.dll
    • É possível fixar código, classes e módulos em flash para reduzir o uso de RAM
    • Oferece suporte opcional a Regex e mapeamento LVGL opcional
  • Com a interface nativa C, ela pode ser embutida como uma biblioteca em código existente, com códigos como o Tasmota servindo de exemplo
    • Oferece easy mapping opcional para chamar código C a partir do Berry
  • Os exemplos de sintaxe mostram iteração sobre listas, criação de maps e iteração por chaves, declaração de class e herança única por meio do operador :
  • Mais códigos de exemplo podem ser vistos no GitHub

1 comentários

 
GN⁺ 2023-10-09
Comentários no Hacker News
  • Para um runtime de 40 KB, é surpreendentemente rico em recursos: uma linguagem parecida com Python/Ruby roda em uma VM com garbage collection e oferece suporte a estilos procedural, orientado a objetos e funcional.
    Mesmo olhando por alto, parece bem confortável de escrever. Em especial, chama a atenção o recurso de criar previamente objetos constantes, colocar a maior parte deles na ROM e usar RAM apenas para dados que de fato podem mudar. Pelo que sei, MicroPython ou Lua não têm algo assim, e em MCUs com bastante ROM/flash, mas pouca RAM, isso faz uma grande diferença.

    • O MicroPython também permite congelar módulos e armazená-los na flash; nesse caso, a flash contém apenas bytecode pré-compilado.
      Se o módulo tiver dados armazenados na forma de objetos imutáveis, como strings ou bytes(), eles são lidos diretamente da flash, sem primeiro serem copiados para a RAM. Porém, para fazer o freeze, é preciso executar o código uma vez em um computador desktop.
      https://docs.micropython.org/en/latest/reference/constrained...
    • Depois de ler a explicação, voltei ao site, e ela me pareceu muito mais abrangente e atraente do que a página inicial.
    • Como @snops explicou, o MicroPython consegue executar código a partir da flash na forma de frozen code. Como consequência, isso precisa ser compilado dentro do binário do firmware, o que desacelera o ciclo rápido de desenvolvimento típico do MicroPython.
      Por isso, um fluxo conveniente é distribuir o código em runtime até a base de código se estabilizar ou o produto se aproximar da etapa de produção e, nesse ponto, congelar o código no firmware. Como modelo ainda mais conveniente, há uma proposta de suporte a mapfs, que aloca parte da flash para armazenamento de código e permite compilar Python para bytecode (mpy_cross), fazer upload para essa área e executá-lo em runtime. A prova de conceito já existe, mas ainda há detalhes a ajustar antes de entrar na mainline.
      https://github.com/micropython/micropython/pull/8381
    • Parece bem interessante e, em vários aspectos, parecido com Lua, mas também há diferenças notáveis. Como referência, no eLua também houve trabalho para colocar alguns dados de forma transparente na ROM.
      Veja os rotables em https://eluaproject.net/doc/v0.9/en_arch_ltr.html
  • Berry é usado no Tasmota: https://tasmota.github.io/docs/Berry/

    • Tasmota parece interessante. Se você já usou ou comparou com toit/jaguar, gostaria de saber o que acha.
  • Parece muito bom. Pessoalmente, acho que a documentação está perto do melhor nível entre as que já vi.
    Gosto especialmente do “short manual”, que permite a um desenvolvedor experiente pegar rapidamente a ideia da linguagem. Eu não conhecia o Tasmota antes, mas acho que vou acabar procurando uma desculpa para usá-lo em algum projeto.

  • O que mais me interessa é saber como ficam o desempenho e o uso de memória em comparação com Lua, e até que ponto é possível fazer sandboxing.
    Também me pergunto se dá para executar código não confiável.

    • A comparação com Lua é a mais interessante. Na maioria das empresas em que trabalhei, Python era usado como uma espécie de proxy tosco ou ferramenta de CLI, mas Lua era difícil de recomendar porque a orientação a objetos baseada em metatables era confusa.
      Se esta linguagem for uma alternativa viável com structs simples e construtores básicos, acho que daria para recomendá-la para substituir scripts Python e suas dependências.
    • Por padrão, o código consegue abrir e escrever no sistema de arquivos, então código não confiável não pode ser executado diretamente.
      O sandboxing precisa ficar a cargo da aplicação. Para sandboxing, WebAssembly é uma boa alternativa.
    • Se incluir gerenciamento nativo de threads com a palavra-chave async, acho que ela se diferenciaria claramente de Lua para uso em engines de jogos.
  • Se fosse quase igual a isso, mas com menos paradigmas e tipagem estática, seria excelente como linguagem de configuração.
    Em vários projetos que precisavam de configurações complexas, houve casos em que deixavam usar Lua ou Starlark/Python em vez de escrever centenas de linhas de YAML, e parecia muito melhor. Mas sempre sinto falta de autocompletar e reflexão. Não vejo bons candidatos exatamente para esse uso, e quase todas as pequenas linguagens de script embutíveis são de tipagem dinâmica.

    • Venho pensando em algo parecido, mas mais próximo de configuração fortemente validada do que de tipagem estática. Seria melhor conseguir carregar a configuração inteira e então gerar uma lista de problemas; se for bem feito, também deveria permitir bom suporte em editores.
      Ainda assim, https://dhall-lang.org/ mostra que muitas configurações podem ser tratadas com tipagem estática, com grandes benefícios. Parece ser possível embuti-lo programaticamente em várias linguagens.
      https://docs.dhall-lang.org/howtos/How-to-integrate-Dhall.ht...
  • Parece bom. Ainda tenho bastante simpatia pelo uLisp (http://www.ulisp.com), mas é legal ver algo assim aparecendo.

  • Se você costuma olhar primeiro exemplos de código para sentir a linguagem, vale ver o diretório de exemplos: https://github.com/berry-lang/berry/tree/master/examples

  • Seria ótimo se oferecessem bindings para outras linguagens. Nós usamos quickjs em Rust, e funciona muito bem.
    Como dá para expor à VM apenas o que queremos, também é possível executar código não confiável.

  • Parece uma linguagem otimizada para uso embarcado. O design e a documentação são bons, e ela não parece fazer nada estranho ou inesperado.
    A sintaxe também é agradavelmente minimalista e combina com meu gosto. Com certeza vou mantê-la em mente no meu próximo projeto com ESP32.

    • Vou experimentar em breve. Eu estava mexendo em um kit de desenvolvimento ESP-32 para fazer decorações de Halloween piscantes, e estou animado para testar isso.
      Parece encaixar muito bem nesse nicho, especialmente porque faz coisas para as quais não me vêm à cabeça equivalentes diretos em outras linguagens.
  • Fico curioso se há uma abordagem padrão para mostrar nomes de funções de script nos frames de stack em stack traces nativos.
    Em caso de crash ou ao usar um profiler de CPU, gostaria de saber se dá para mostrar stack traces nativos e stack traces de script misturados.