Linguagem Starlark

Informar um problema Mostrar fonte Por noite · 7,4 do Google. 7.3 · 7.2 · 7.1 · 7.0 · 6.5

Esta página é uma visão geral do Starlark, antes conhecida como Skylark, a linguagem usada no Bazel. Para uma lista completa e tipos, consulte a Referência da API do Bazel.

Para saber mais sobre a linguagem, consulte o repositório do Starlark no GitHub (link em inglês).

Para a especificação oficial da sintaxe do Starlark e do usuário, consulte a Especificação de linguagem Starlark.

Sintaxe

A sintaxe do Starlark é inspirada no Python3. Esta é uma sintaxe válida no Starlark:

def fizz_buzz(n):
  """Print Fizz Buzz numbers from 1 to n."""
  for i in range(1, n + 1):
    s = ""
    if i % 3 == 0:
      s += "Fizz"
    if i % 5 == 0:
      s += "Buzz"
    print(s if s else i)

fizz_buzz(20)

A semântica do Starlark pode ser diferente do Python, mas as diferenças comportamentais são raras, exceto nos casos em que o Starlark gera um erro. O código Python a seguir têm suporte:

Mutabilidade

Starlark favorece a imutabilidade. Duas estruturas de dados mutáveis estão disponíveis: listas e dicionários. Mudanças em estruturas de dados mutáveis, como anexar um valor a uma lista ou excluir uma entrada em um dicionário, são válidas apenas para objetos criados no contexto atual. Depois que um contexto é concluído, os valores dele se tornam imutáveis.

Isso ocorre porque os builds do Bazel usam a execução paralela. Durante um build, cada .bzl e cada arquivo BUILD recebem o próprio contexto de execução. Cada regra também é analisados em seu próprio contexto.

Vejamos um exemplo com o arquivo foo.bzl:

# `foo.bzl`
var = [] # declare a list

def fct(): # declare a function
  var.append(5) # append a value to the list

fct() # execute the fct function

O Bazel cria var quando foo.bzl é carregado. var faz parte do contexto de foo.bzl. Quando fct() é executado, isso ocorre no contexto de foo.bzl. Depois que a avaliação de foo.bzl for concluída, o ambiente vai conter uma entrada imutável, var, com o valor [5].

Quando outra bar.bzl carrega símbolos de foo.bzl, os valores carregados permanecem imutáveis. Por esse motivo, o seguinte código em bar.bzl é ilegal:

# `bar.bzl`
load(":foo.bzl", "var", "fct") # loads `var`, and `fct` from `./foo.bzl`

var.append(6)  # runtime error, the list stored in var is frozen

fct()          # runtime error, fct() attempts to modify a frozen list

As variáveis globais definidas em arquivos bzl não podem ser alteradas fora do arquivo bzl que as definiu. Assim como no exemplo acima que usa arquivos bzl, os valores retornados pelas regras são imutáveis.

Diferenças entre arquivos BUILD e .bzl

Os arquivos BUILD registram as metas fazendo chamadas para regras. Os arquivos .bzl fornecem definições de constantes, regras, macros e funções.

As funções nativas e as regras nativas são símbolos globais em arquivos BUILD. Os arquivos bzl precisam ser carregados usando o módulo native.

Há duas restrições sintáticas em arquivos BUILD: 1) declarar funções é ilegal e 2) os argumentos *args e **kwargs não são permitidos.

Diferenças com o Python

  • As variáveis globais são imutáveis.

  • Não são permitidas instruções for no nível superior. Use-os nas funções como alternativa. Em arquivos BUILD, é possível usar compreensões de lista.

  • Não são permitidas instruções if no nível superior. No entanto, as expressões if podem ser usadas: first = data[0] if len(data) > 0 else None.

  • Ordem determinística para iterar dicionários.

  • A recursão não é permitida.

  • O tipo int é limitado a números inteiros assinados de 32 bits. Os estouros vão gerar um erro.

  • Modificar uma coleção durante a iteração é um erro.

  • Exceto em testes de igualdade, os operadores de comparação <, <=, >=, > etc. são não definidos em nenhum tipo de valor. Resumindo: 5 < 'foo' vai gerar um erro e 5 == "5" vai retornar "falso".

  • Em tuplas, uma vírgula final é válida apenas quando a tupla está entre parênteses, quando você escreve (1,) em vez de 1,.

  • Os literais de dicionário não podem ter chaves duplicadas. Por exemplo, este é um erro: {"a": 4, "b": 7, "a": 1}.

  • As strings são representadas com aspas duplas (como quando você chama repr).

  • Strings não são iteráveis.

Os seguintes recursos do Python não são compatíveis:

  • concatenação implícita de strings (use o operador + explícito).
  • Comparações conectadas (como 1 < x < 5).
  • class (consulte a função struct).
  • import (consulte a instrução load).
  • while, yield.
  • tipos flutuantes e de conjuntos.
  • geradores e expressões geradoras.
  • is (use ==).
  • try, raise, except, finally (consulte fail para ver erros fatais).
  • global, nonlocal.
  • a maioria das funções integradas e a maioria dos métodos.